《APUE》Chapter 8 Process control(学习笔记加上自己的代码)

时间:2021-04-21 22:49:46

Process  Control

This  includes the creation of new processes, program execution, and process termination.



Process  Identifiers ---- PID(嘿嘿,和自控里面的不同哇。。。)


Every process has a unique process ID, a non-negative integer . Most UNIX  systems  implement  algorithms  to  delay reuse.This  prevents  a  new  process  from  being  mistaken for the previous process to have used the same ID.

<span style="font-size:14px;">#include <unistd.h>
pid_t getpid(void);
Returns: process ID of calling process
pid_t getppid(void);
Returns: parent process ID of calling process
uid_t getuid(void);
Returns: real user ID of calling process
uid_t geteuid(void);
Returns: effective user ID of calling process
gid_t getgid(void);
Returns: real group ID of calling process
gid_t getegid(void);
Returns: effective group ID of calling process</span>

值得注意的就是这些函数都是没有错误返回的!

test code:
include <fcntl.h>
#include <stdio.h>

int main()
{
        printf("current process ID:%d\n",getpid());
        printf("parent  process ID:%d\n",getppid());
        printf("real    user    ID:%d\n",getuid());
        printf("effective user  ID:%d\n",geteuid());
        printf("real group      ID:%d\n",getgid());
        printf("effective user  ID:%d\n",getegid());
        return 0;
}


test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out
current process ID:15278
parent  process ID:12931
real user      ID:1000
effective user      ID:1000
real group       ID:1000
effective user      ID:1000




fork Function

An existing process can create a new one by calling thefork function.
#include <unistd.h>
pid_t fork(void);
Returns: 0 in child, process ID of child in parent,−1 on error


The new process created by fork is called the child process.This function is called once but returns twice.


There a son fork returns  0  to  the  child  is  that  a  process  can  have  only  a single  parent,  and  the  child  can always  call getppid to  obtain  the  process  ID  of  its parent.  (Process ID 0 is reserved for use by the kernel, so it’s not possible for 0 to be the process ID of a child.)


Modern implementations don’t perform a complete copy of the parent’s data, stack, and  heap,  since  a fork is  often followed  by  an exec. Instead,  a  technique  called copy-on-write (COW) is used. These regions are shared by the parent and the child and have  their  protection  changed  by  the  kernel  to  read-only .If either  process  tries  to
modify  these  regions,  the  kernel  then  makes  a  copy  of  that  piece  of  memory  only, typically a ‘‘page’’ in a virtual memory system.


<span style="font-size:14px;">#include"apue.h"
int glob = 6;
char buf[] = "a write to stdout\n";

int main(int argc ,char* argv[])
{
        int var;
        pid_t pid;
        var = 88;

        if(write(STDOUT_FILENO,buf,sizeof(buf)-1) != sizeof(buf)-1)
        {
                printf("write error");
        }
        printf("before fork\n");

        if((pid = fork()) < 0)
        {
                printf("fork error");
        }
        else if(pid == 0)
        {
                glob++;
                var++;
        }
        else
        {
                sleep(2);
        }

        printf("pid = %d glob = %d var = %d\n",getpid(),glob,var);

        return 0;
}</span><span style="font-size:18px;">
</span>
When  we  write  to  standard output,  we  subtract  1  from  the  size  of  buf to  avoid writing the terminating null byte. Although strlen will calculate the length of a string

test result:
liuzjian@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out
a write to stdout
before fork
pid = 15338 glob = 7 var = 89
pid = 15337 glob = 6 var = 88

有个很有意思的现象:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out > temp.txt
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ cat ./temp.txt
a write to stdout
before fork
pid = 15383 glob = 7 var = 89
before fork
pid = 15382 glob = 6 var = 88


把输出重定向到一个文本文件就出现两个before fork 了。。。纠结了大概10分钟。。。无比傻逼的跑去问别人。。其实很简单。。。
when we  run  the program  interactively, we get  only  a  single  copy  of  the  first printf line,  because the standard output buffer is flushed by the newline. When we redirect standard output to a file, however, we get two copies of the printf line.



File Sharing


Besides the open files, numerous other properties of the parent are inherited by the child:
•Real user ID, real group ID, effective user ID, and effective group ID
•Supplementary group IDs
•Process group ID
•Session ID
•Controlling terminal
•The set-user-ID and set-group-ID flags
•Current working directory
•Root directory
•File mode creation mask
•Signal mask and dispositions
•The close-on-exec flag for any open file descriptors
•Environment
•Attached shared memory segments
•Memory mappings
•Resource limits

The differences between the parent and child are
•The return values from fork are different.
•The process IDs are different.
•The two processes have different parent process IDs: the parent process ID of the child is the parent; the parent process ID of the parent doesn’t change.
•The  child’s tms_utime, tms_stime, tms_cutime, and tms_cstime values are set to 0 
•File locks set by the parent are not inherited by the child.
•Pending alarms are cleared for the child.
•The set of pending signals for the child is set to the empty set.


There are two uses for fork:
1.  When a process  wants  to  duplicate  itself  so  that  the  parent  and  the  child  can each  execute  different  sections  of  code  at  the  same  time.  
2.  When a process  wants  to  execute  a  different  program. 



vfork  Function


The vfork function  creates  the new  process,  just like fork,without  copying  the  address  space  of  the  parent into  the child,  as  the  child  won’t  reference  that  address  space;  the  child  simply  calls exec (or exit)right after the vfork. Instead, the child runs in the address space of the parent until  it  calls  either exec or exit

_exit does  not  perform  any  flushing  of  standard I/O  buffers. 


If the implementation also closes the standard I/O streams,  however ,t he  memory  representing  the FILE object  for the  standard output will be cleared out. Because the child is borrowing the parent’s address space, when the parent resumes  and  calls printf, no output  will  appear  and printf will  return −1. Note  that  the parent’s STDOUT_FILENO is  still  valid,  as  the  child  gets  a  copy  of  the parent’s file descriptor array

#include"apue.h"
#include"myerr.h"
int glob = 6;

int main()
{
        int var;
        pid_t pid;

        var = 88;
        printf("before fork!\n");
        if((pid = vfork()) < 0)
        {
                err_sys("vfork error\n");
        }
        else if(pid == 0)
        {
                glob++;
                var++;
                _exit(0);
        }
        printf("pid = %d,glob = %d,var = %d\n",getpid(),glob,var);
        exit(0);
}


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out
before fork!
pid = 15758,glob = 7,var = 89

       Another  difference  between  the  two  functions  is  that vfork guarantees  that  the child runs first, until the child calls exec or exit.

exit Functions


A process can terminate normally in five ways:

1.  Executing a return from  the main function. This is equivalent to calling exit.

2.  Calling the exit function. This function  is  defined  by  ISO  C  and  includes  the calling  of  all  exit  handlers  that have  been  registered  by  calling at exit and closing  all  standard I/O  streams. 

3.  Calling the _exit or _Exit function. _Exit to  provide  a  way for  a  process  to  terminate  without  running  exit  handlers  or  signal  handlers. Whether standard I/O streams are flushed depends on the implementation. 

4.  Executing a return from the start routine of the last thread in the process.  

5.  Calling the pthread_exit function  from  the  last  thread  in  the  process.

The three forms of abnormal termination are as follows:

1.  Calling abort

2. When the process receives certain signals.

3.  The last  thread  responds  to  a  cancellation  request. 


Regardless  of  how  a  process  terminates,  the  same  code  in  the  kernel  is  eventually
executed.  This kernel  code  closes  all  the  open  descriptors  for  the  process,  releases  the
memory that it was using, and so on

  In the case of an abnormal  termination,  however,the  kernel—not  the  process — generates a termination
status to indicate the reason for the abnormal termination.

  But what happens if the parent terminates before the child? The answer is  that  the init process  becomes  the  parent  process  of  any  process  whose  parent terminates.  In such a case, we say that the process has been inherited by init.
    
      What happens when a process that has been inherited by init terminates?  Does it become a zombie? The answer is ‘‘no,’ ’because init is  written  so  that  whenever  one  of  its  children  terminates, init calls  one  of  the
wait functions to fetch the termination status.





wait and waitpid Functions


When a process terminates, either normally or abnormally,the kernel notifies the parent by sending the SIGCHLD signal to the parent

 we need  to  be  aware that  a  process  that  calls wait or waitpid can

•Block, if all of its children ar estill running

•Return  immediately  with  the  termination  status  of  a  child,  if  a  child  has  terminated and is waiting for its termination status to be fetched

•Return immediately with an error, if it doesn’t have any child processes



If the process is calling wait because it received the SIGCHLD signal, we expect wait to return immediately .But if we call it at any random point in time, it can block.

<span style="font-size:14px;">#include <sys/wait.h>
pid_t wait(int *statloc );
pid_t waitpid(pid_t pid,int *statloc ,int options );
Both return: process ID if OK, 0 (see later), or −1 on error</span>
下面是linux里面对于waitpid的解释
/* Wait for a child matching PID to die.
   
  If PID is greater than 0, match any process whose process ID is PID.
   If PID is (pid_t) -1, match any process.
   If PID is (pid_t) 0, match any process with the
   same process group as the current process.
   If PID is less than -1, match any process whose
   process group is the absolute value of PID.
   If the WNOHANG bit is set in OPTIONS, and that child
   is not already dead, return (pid_t) 0.  If successful,
   return PID and store the dead child's status in STAT_LOC.
   Return (pid_t) -1 for errors.  If the WUNTRACED bit is
   set in OPTIONS, return status for stopped children; otherwise don't.

   This function is a cancellation point and therefore not marked with
   __THROW.  */

下面是对于wait的解释
/* Wait for a child to die.  When one does, put its status in *STAT_LOC
   and return its process ID.  For errors, return (pid_t) -1.


   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern __pid_t wait (__WAIT_STATUS __stat_loc);


《APUE》Chapter 8 Process control(学习笔记加上自己的代码)


最后一个WIFCONTINUED没怎么懂,job control还布吉岛。。。

<span style="font-size:14px;">#include"apue.h"
#include"pr_exit.h"
#include"myerr.h"
#include<sys/wait.h>


int main ()
{
        pid_t pid;
        int status;
        if((pid = fork()) < 0)
        {
                err_sys("fork error\n");
        }
        else if(pid == 0)
        {
                exit(7);
        }


        if(wait(&status) != pid)
        {
                err_sys("wait pid\n");
        }
        pr_exit(status);


        if((pid = fork()) < 0)
        {
                err_sys("wait error\n");
        }
        else if(pid == 0)
        {
                abort();
        }


               if(wait(&status) != pid)
        {
                err_sys("wait pid\n");
        }
        pr_exit(status);


        if((pid = fork()) < 0)
        {
                err_sys("fork error\n");
        }
        else if(pid == 0)
        {
                status /= 0;
        }




        if(wait(&status) != pid)
        {
                err_sys("wait error\n");
        }
        pr_exit(status);


        exit(0);
}</span>
pr_exit.h 文件的实现:
#ifndef _PR_EXIT_H
#define _PR_EXIT_H

#include"apue.h"
#include<sys/wait.h>
void pr_exit(int status)
{
        if(WIFEXITED(status))
        {
                printf("normal termination,exit status = %d\n",WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status))
        {
                printf("abnormal termination,signal number =%d%s\n",WTERMSIG(status),
#ifdef WCOREDUMP
                WCOREDUMP(status) ? "(core file generated)" : " ");
        }
#else
                " ");
        }
#endif
        else if(WIFSTOPPED(status))
        {
                printf("child stopped,signal number = %d\n",WSTOPSIG(status));
        }
}
#endif

test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out
normal termination,exit status = 7
abnormal termination,signal number =6 
abnormal termination,signal number =8 


<span style="font-size:14px;">#include"apue.h"
#include"myerr.h"
#include"stdio.h"
#include<sys/wait.h>
 
int main()
{
        pid_t pid;
 
        if((pid = fork()) < 0)
        {
                err_sys("forkerror!\n");
        }
        else if(pid == 0)
        {
                if((pid = fork()) < 0)
                {
                        err_sys("forkerror\n");
                }
                else if(pid > 0)
                {
        /*              if(waitpid(pid,NULL,0) != pid)
                        {
                               printf("waitpid error\n");
                        }
       
                        printf("firstchild pid = %d\n",getpid());
        */
                        exit(0);
                }
 
                sleep(2);
                printf("\nsecond child, parentgetppid =%d second child pid = %d\n",getppid(),getpid());
                exit(0);
        }
 
        if(waitpid(pid,NULL,0) != pid)
        {
                err_sys("waitpiderror\n");
        }
        printf("parent pid = %d\n",getppid());
        exit(0);
}</span>



The wait()system call suspends execution of the calling process until one of its childrenterminates.  The call wait(&status) is equivalent to:   waitpid(-1, &status, 0);

 

 

 

waitid Function

 

#include <sys/wait.h>
int waitid(idtype_t idtype ,id_t id ,siginfo_t *infop,int options );
Returns: 0 ifOK,−1 on error


 

Instead of encoding this information in a single argument combined with the process ID or process group ID, two separate arguments are used.

 

<span style="font-size:14px;">/*************************************************************
code writer :EOF
code date :2014.03.28
e-mail:jasonleaster@gmail.com
code purpose :
        I just share my demo with who isinteresting in APUE.
Open source makethe world more beautiful. I would like to
recept yourfeedback, if there is something wrong with my code.
Please touch meby e-mail. Thank you
 
*************************************************************/
#include<stdio.h>
#include<apue.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<signal.h>
 
int main()
{
        int pid = 0;
        int status = 0;
        siginfo_t* p_siginfo = NULL;
 
        p_siginfo =(siginfo_t*)malloc(sizeof(siginfo_t));
 
        if(p_siginfo == NULL)
        {
                printf("malloc error\n");
                return 0;
        }
 
        if((pid = fork()) < 0)
        {
                printf("forkerror\n");
        }
        else if(pid == 0)
        {
                printf("hello! I am thechild %d\n",getpid());
                exit(0);
        }
 
/*      if(waitpid(pid,&status,0) < 0 )
        {
                printf("waitpiderror\n");
        }
*/
 
        int temp = 0;
        if((temp =waitid(P_PID,pid,p_siginfo,0)) < 0)// why there would return -1
        {
                printf("waitiderror\n");
        }
 
        if(WIFEXITED(status))
        {
                printf("normallytermination,exit status = %d\n",status);
        }
 
        printf("parent exit\n");
 
        free(p_siginfo);
        return 0;
}</span>


 

写了个demo,但是不知道waitid的哪个地方错了。老是提示waitid错误

 

 

Race Conditions

For  our purposes,  a  race condition  occurs  when multiple  processes  aretrying to  do something  with shared  data  and the final  outcome depends  on  the order  in  which the processes run. 

</pre><pre name="code" class="cpp">#include <apue.h>
#include<stdio.h>
#include<myerr.h>
 
static void charatatime(char*);
 
int main()
{
        pid_t pid;
 
        if((pid = fork()) < 0)
        {
                err_sys("forerror\n");
        }
        else if(pid == 0)
        {
                charatatime("output fromchild\n");
        }
        else
        {
                charatatime("output fromparent\n");
        }
 
        return 0;
}
 
static void
charatatime(char*str)
{
        char * ptr;
        int c ;
 
        setbuf(stdout,NULL);
        for(ptr = str;(c = *ptr++) != 0;)
        {
                putc(c,stdout);
        }
}




Test result

liuzjian@ubuntu:/Ad_Pro_in_Unix/chapter_8$./a.out

ououttppuutt  ffrom child

rom parent



We  set  the  standardoutput  unbuffered,  so  every  character  output  generates  a write.
看到这句话我才发现原来write是不会buffer的。。。即刻写出。



exec Functions


When a process  calls  one  of  the exec functions,  that  process  is  completely replaced  by  the  new  program, and the  new  program  starts  executing  at  its main function.  The process ID does not change across an exec,because a new process is not created; 

With fork, we can create new processes; and with the exec functions, we can initiate new programs.


#include <unistd.h>
int execl(const char *pathname ,const char *arg0 ,... /* (char *)0 */ );
int execv(const char *pathname ,char *constargv []);
int execle(const char * pathname ,const char *arg0 ,...
/* (char *)0, char *constenvp [] */ );
int execve(const char * pathname ,char *constargv [], char *constenvp []);
int execlp(const char * filename,const char *arg0 ,... /* (char *)0 */ );
int execvp(const char * filename,char *constargv []);
int fexecve(intfd ,char *constargv [], char *constenvp []);
All seven return:−1 on error, no return on success



值得注意的是exe*执行正确的情况下是没有返回值的

filename 和pathname是有区别的哇。。。其实我一开始觉得没啥区别都是./*** 有区别的

•If filename contains a slash, it is taken as a pathname.

•Otherwise,  the  executable  file  is  searched  for  in  the  directories  specified  by  the PATH environment variable.


PATH=/bin:/usr/bin:/usr/local/bin/:.

The last  path  prefix  specifies  the  current  directory


If either execlp or execvp finds an executable file using one of the path prefixes, but the file isn’t a machine executable that was generated by the link editor, the function assumes  that  the  file  is  a  shell  script  and  tries  to invoke /bin/sh with  the filename as input to the shell.


The three functions whose names end in an e (execle, execve, and fexecve)allow us to pass  a  pointer  to  an  array  of  pointers  to  the  environment  strings.


We’ve  mentioned  that  the  process  ID  does  not  change  after  an exec,but  the  new program inherits additional properties from the calling process:
•Process ID and parent process ID
•Real user ID and real group ID
•Supplementary group IDs
•Process group ID
•Session ID
•Controlling terminal
•Time left until alarm clock
•Current working directory
•Root directory
•File mode creation mask
•File locks
•Pro cess signal mask
•Pending signals
•Resource limits
•Nice value (on XSI-conformant systems; see Section 8.16)
•Values for tms_utime, tms_stime, tms_cutime,andtms_cstime

every open descriptor in a process has a close-on-exec flag. If this flag is  set,  the  descriptor  is  closed 
across  an exec.Otherwise,  the  descriptor  is  left  open across the exec.The default is to leave the descriptor open across the exec unless we specifically set the close-on-exec flag using fcntl.



注意其他几个exec*函数是由execv这个system call 实现的。其他几个都是库函数
《APUE》Chapter 8 Process control(学习笔记加上自己的代码)



<span style="font-size:14px;">/****************************************************************
code writer : EOF
code date : 2014.03.27
e-mail: jaosonleaster@gmail.com
attention:
        The ./a.out ./b.out problem is just simple "hello world"
program.If you want to use another program to repleace the program 
it is OK.
        
****************************************************************/
#include"apue.h"
#include"myerr.h"
#include"sys/wait.h"
char* env_init[] = {"USER=unknown" ,"PATH=/tmp",NULL};


int main()
{
        pid_t  pid;
        if((pid = fork()) < 0)
        {
                err_sys("fork error\n");
        }
        else if( pid == 0)
        {
                if(execle("./a.out","a.out",(char*)0,env_init) < 0)
                {
                        err_sys("execle error\n");
                }
        }
        else
        {
                if(waitpid(pid,NULL,0) < 0)
                {
                        err_sys("wait error\n");
                }
                printf("parent\n");
        }


        if((pid = fork()) < 0)
        {              
		  err_sys("fork error\n");
        }
        else if(pid == 0)
        {
                if(execlp("./b.out","b.out",(char*) 0) < 0)
                {
                        err_sys("execlp error\n");
                }
        }
        else
        {
                if(waitpid(pid,NULL,0) < 0)
                {
                        err_sys("wait error\n");
                }
                printf("parent\n");
        }


        exit(0);
}</span>
test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./c.out
The first hello world!
parent
The second hello world!
parent













Changing User  IDs  and  Group  IDs


<span style="font-size:14px;">#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
Both return: 0 if OK, −1 on error</span>

1.  If the  process  has  superuser  privileges,  the setuid function  sets  the  real  user ID, effective user ID, and saved set-user-ID to uid.
2.  If the process does not have superuser privileges, but uid equals either the real user  ID  or  the  saved  set-user-ID, setuid sets  only  the  effective  user  ID  to uid. The real user ID and the saved set-user-ID are not changed.
3.  If neither  of  these  two  conditions  is  true, errno is  set  to EPERM and −1 is returned.


  setuid()  sets the effective user ID of the calling process.  If the effective UID of the caller is root, the real UID and saved set-user-ID are also set.
APUE 没给demo,我每次遇到各种real ID effective ID saved ID就捉急。。。。《APUE》Chapter 8 Process control(学习笔记加上自己的代码)我都不知道什么时候我写程序会去set the process ID。。。我去。。。
这一小节读的很失败。。。

Interpreter Files
 
These files are text files that begin with a line of the form:
#! pathname  [ optional-argument ]

惊叹号之后的的空格可有可无

The recognition of these files is done within the kernel as part of processing the exec system call. The actual file that gets executed by the kernel is not the interpreter file, but rather the file specified by the pathname on the first  line  of  the  interpreter  file. 

Be  sure to differentiate  between  the  interpreter  file—a text file that begins with #!—and the interpreter,which is specified by the pathname on the first line of the interpreter file

something like this one:
a  interpreter file
<span style="font-size:14px;">#! /usr/bin/awk -f 
BEGIN
{
        for(i = 0;i< ARGC;i++)
        {
                printf("ARGV[%d] = %s\n",i,ARGV[i]);
        }
        exit(0);
}</span><span style="font-size:18px;">
</span>

test code:
<span style="font-size:14px;">#include"apue.h"
#include"sys/wait.h"
#include"myerr.h"

int main()
{
        pid_t pid;

        if((pid = fork()) < 0)
        {
                err_sys("fork error!\n");
        }
        else if(pid == 0)
        {
                //if(execl("/Ad_Pro_in_Unix/chapter_8/awkexample","awkexample",NULL) < 0)//this is for figure-21
                if(execl("./testinterp","testinterp",NULL) < 0)//this is fir figure-20
                {
                        err_sys("execl error!\n");
                }
        }

        if(waitpid(pid,NULL,0) < 0)
        {
                err_sys("waitpid error!\n");
        }
        exit(0);
}</span><span style="font-size:18px;">
</span>
testinterp:

#! ./a.out

就这么简单,理解interpreter file概念就好


Using  an  interpreter
script, however, we can simply write
#!/bin/csh

(C shell script follows in the interpreter file)












system Function


#include <stdlib.h>
int system(const char * cmdstring);
Returns: (see below)


Because system is  implemented  by  calling fork, exec,and waitpid, there are three types of return values.

1.  If either the fork fails or waitpid returns an error other than EINTR, system are turns −1 with errno set to indicate the error.

2.  If the exec fails, implying that the shell can’t be executed, the return value is as if the shell had executed exit(127).

3.  Otherwise, all  three  functions—fork, exec,and waitpid—succeed,  and  the return  value  from system is  the  termination  status  of  the  shell,  in  the  format specified for waitpid.

APUE给出的system的一个简单实现(不带signal处理功能)

<span style="font-size:14px;">#include<sys/wait.h>
#include<errno.h>
#include<unistd.h>

int system(const char* cmdstring)
{
        pid_t pid;
        int status;

        if(cmdstring == NULL)
        {
                return (1);
        }

        if((pid = fork()) < 0)
        {
                status = -1;
        }
        else if(pid == 0)
        {
                execl("/bin/sh","sh","-c",cmdstring,NULL);
                _exit(127);
        }
        else
        {
                while(waitpid(pid,&status,0) < 0)
                {
                        if(errno != EINTR)
                        {
                                status = -1;
                                break;
                        }
                }
        }
        return (status);
}</span><span style="font-size:18px;">
</span>

The shell’s -c option tells it to take the next command -line argument—cmdstring,in this case —as its command input instead of reading from standard input or from a given file. 

If  we  didn’t  use  the  shell  to  execute  the  command,  but  tried  to  execute  the command  ourself,  it  would  be more difficult。

Note that we call _exit instead of exit.We do this to prevent any standard I/O buffers,  which  would  have  been  copied  from  the  parent  to  the  child  across  the fork, from being flushed in the child.


 对于上面那个版本system函数测试:
<span style="font-size:14px;">#include<sys/wait.h>
#include<errno.h>
#include<unistd.h>


int system(const char* cmdstring)
{
        pid_t pid;
        int status;


        if(cmdstring == NULL)
        {
                return (1);
        }


        if((pid = fork()) < 0)
        {
                status = -1;
        }
        else if(pid == 0)
        {
                execl("/bin/sh","sh","-c",cmdstring,NULL);
                _exit(127);
        }
        else
        {
                while(waitpid(pid,&status,0) < 0)
                {
                        if(errno != EINTR)
                        {
                                status = -1;
                                break;
                        }
                }
        }
        return (status);
}</span>

test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out
2014年 03月 28日 星期五 20:39:53 CST
normal termination,exit status = 0
sh: 1: nosuchcommand: not found
normal termination,exit status = 127
liuzjian tty7         2014-03-19 18:55 (:0)
liuzjian pts/2        2014-03-28 14:00 (:0.0)
normal termination,exit status = 44

source_a.c
<span style="font-size:14px;">#include"apue.h"
#include"myerr.h"
#include"pr_exit.h"


int main(int argc,char* argv[])
{
        int status;
        if(argc < 2)
        {
                err_quit("command-line argument required");
        }


        if((status = system(argv[1])) < 0)
        {
                err_sys("system() error\n");
        }
        pr_exit(status);


        exit(0);
}</span>


source_b.c
<span style="font-size:14px;">#include"apue.h"
#include"stdio.h"
int main()
{
        printf("real uid = %d,effective uid = %d\n",getuid(),geteuid());
        exit(0);
}</span>

test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out ./b.out
real uid = 1000,effective uid = 1000
normal termination,exit status = 0

                               
The system function should never be used from a set-user-ID or a set-group-ID program










Process  Accounting



APUE没给acct的prototype 。。。。。。。。。。。在linux里面找的
#include <unistd.h>
int acct(const char *filename);
On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.




DESCRIPTION
       The acct() system call enables or disables process accounting.  If called with the name of an existing file as its argument, accounting is turned on, and records for each terminating process are appended to filename as it terminates.  An  argument  of  NULL  causes accounting to be turned off.

acct.h 里面翻出来的acct 结构体的定义
<span style="font-size:14px;">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.  */
};</span>

 


linux 2.4的不支持SCOMPAT 和AEXPND。。。。现在3.0 还是不支持
acct.h里面找到的
<span style="font-size:14px;">enum
  {
    AFORK = 0x01,               /* Has executed fork, but no exec.  */
    ASU = 0x02,                 /* Used super-user privileges.  */
    ACORE = 0x08,               /* Dumped core.  */
    AXSIG = 0x10                /* Killed by a signal.  */
  };</span>


笔记一下有问题的代码:
#include <apue.h>
#include <sys/wait.h>
#include <sys/acct.h>
#include <myerr.h>


#ifdef HAS_SA_STAT
        #define FMT "%-*.*s e = %6ld,chars = %7ld,stat = %3u: %c %c %c %c\n"
#else
        #define FMT "%-*.*s e = %6ld,chars = %7ld,%c %c %c %c\n"
#endif


#ifndef HAS_ACORE
        #define ACORE 0
#endif


#ifndef HAS_AXSIG
        #define AXSIG 0
#endif


static unsigned long
compt2ulong(comp_t comptime)
{
        unsigned long val;
        int exp;
        val = comptime & 0x1fff;
        exp = (comptime >> 13) & 7;
        while(exp-- >0)
        {
                val *= 8;
        }
        return (val);
}


int main(int argc,char* argv[])
{
        struct acct acdata;
        FILE *fp;


        if(argc != 2)
        {
                err_quit("usage: pracct filename");
        }


        if((fp = fopen(argv[1],"r")) == NULL)
        {
                err_sys("can't open %s",argv[1]);
        }
        while(fread(&acdata,sizeof(acdata),1,fp) ==1)
        {
                printf(FMT,(int)sizeof(acdata.ac_comm),(int)sizeof(acdata.ac_comm),acdata.ac_comm,compt2ulong(acdata.ac_etime),compt2ulong(acdata.ac_io),
#ifdef HAS_STAT
        (unsigned char) acdata.ac_stat,
#endif
        acdata.ac_flag & ACORE ? 'D' :' ',
        acdata.ac_flag & AXSIG ? 'X' :' ',
        acdata.ac_flag & AFORK ? 'F' :' ',
        acdata.ac_flag & ASU   ? 'S' :' ');
        }




        if(ferror(fp))
        {
                err_sys("read error\n");
        }


        exit(0);
}

我始终不明白上面这段代码什么APUE给出的是fread一个argv[1] 这个我猜测就是regular file但是空文本有什么用呢。抑或说读的是特殊的文件?APUE没有给出操作过程,只有最后一个结果显示。。。让我非常头疼

执行./a.out  ./test.c  这里test.c 就是个简单的hello world 程序的source code输出很奇怪。。。



User Identification:


#include <unistd.h>
char *getlogin(void);
Returns: pointer to string giving login name if OK,NULL on error

还有一种方式返回user name。。。
getpwuid(getuid()) 利用这个返回的passwd结构体成员pw_name
       struct passwd *getpwuid(uid_t uid);
 
       The getpwuid() function returns a pointer to a structure containing the broken-out fields of the record in the password database that matches the user ID uid.

       The passwd structure is defined in <pwd.h> as follows:

           struct passwd {
               char   *pw_name;       /* username */
               char   *pw_passwd;     /* user password */
.....


<span style="font-size:14px;">#include <stdio.h>
#include <unistd.h>

int main()
{
        printf("the user login: %s\n",getlogin());
        return 0;
}</span><span style="font-size:18px;">
</span>

test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out
the user login: jasonleaster




Process  Times


感觉times这个函数遇到很多次了。。。。。

#include <sys/times.h>
clock_t times(struct tms *buf );
Returns: elapsed wall clock time in clock ticks if OK,−1 on error

struct tms {
clock_t  tms_utime;  /* user CPU time */
clock_t  tms_stime;  /* system CPU time */
clock_t  tms_cutime; /* user CPU time, terminated children
clock_t  tms_cstime; /* system CPU time, terminated childr
};



#include"apue.h"
#include"myerr.h"
#include"pr_exit.h"
#include <sys/times.h>


static void pr_times(clock_t,struct tms*, struct tms*);
static void do_cmd(char*);


int main (int argc ,char* argv[])
{
        int i;


        setbuf(stdout,NULL);
        for(i =1;i< argc ;i++)
        {
                do_cmd(argv[i]);
        }
        exit(0);
}


static void
do_cmd(char* cmd)
{
        struct tms tmsstart,tmsend;
        clock_t start,end;
        int status; 
        
        printf("\ncommand:%s\n",cmd);
        
        if((start = times(&tmsstart)) == -1)
        {
                err_sys("times error\n");
        }
        
        if((status = system(cmd)) < 0)
        {
                err_sys("system() error\n"); 
        }

        if((end = times(&tmsend)) == -1)
        {
                err_sys("times error\n");
        }


        pr_times(end-start,&tmsstart,&tmsend);
        pr_exit(status);
}


static void
pr_times(clock_t real,struct tms* tmsstart,struct tms* tmsend)
{
        static long clktck = 0;


        if(clktck == 0)
        {
                if((clktck = sysconf(_SC_CLK_TCK)) < 0)
                {
                        err_sys("sysconf error\n");
                }


                printf("  real: %7.2f\n",real / (double) clktck);
                printf("  user: %7.2f\n",
                        (tmsend->tms_utime - tmsstart->tms_utime) / (double) clktck);


                printf("  sys: %7.2f\n",
                        (tmsend->tms_stime - tmsstart->tms_stime) / (double) clktck);


                printf("  child user: %7.2f\n",
                        (tmsend->tms_cutime - tmsstart->tms_cutime) / (double) clktck);


                printf("  child sys: %7.2f\n",
                        (tmsend->tms_cstime - tmsstart->tms_cstime) / (double) clktck);


        }
}

test result :
liuzjian@ubuntu:/Ad_Pro_in_Unix/chapter_8$ ./a.out "date" "pwd"


command:date
2014年 03月 28日 星期五 23:35:50 CST
  real:    0.01
  user:    0.00
  sys:    0.00
  child user:    0.00
  child sys:    0.00
normal termination,exit status = 0


command:pwd
/Ad_Pro_in_Unix/chapter_8
normal termination,exit status = 0


也不知道是进程控制这章比较重要话的时间长,还是自己拖拉。。。拖拖搞搞,两天才弄完。。。T_T

如果有什么问题关于APUE的非常欢迎讨论,这篇笔记如果有错漏非常欢迎viewer 批评指正

The . L
             2014.03.28 于XTU 夜



《APUE》Chapter 8 Process control(学习笔记加上自己的代码)