Process Relationships
Terminal Logins
Unix 是1971年开始的吧,但是这个terminal login的机制最起码30年没变过。。。非常成熟的机制
All the processes shown in Figure9.1 have a real user ID of 0 and an effective user ID of 0(i.e., they all have superuser privileges). The init process also execs the getty program with an empty environment.
It is getty that calls open for the terminal device. The terminal is opened for reading and writing.
Once the device is open, file descriptors 0, 1, and 2 are set to the device. Then gettyoutputs something like
login: and waits for us to enter our user name。
something like this:
All the processes shown in Figure9.2 have superuser privileges, since the original init process has superuser privileges. The process ID of the bottom three processes in Figure9.2 is the same, since the process ID does not change across an exec.Also, all the processes other than the original init process have a parent process ID of 1.
If we log in correctly, login will
•Change to our home directory(chdir)
•Change the ownership of our terminal device (chown) so we own it
•Change the access permissions for our terminal device so we have permission to read from and write to it
•Set our group IDs by calling setgid and initgroups
•Initialize the environment with all the information that loginhas: our home directory (HOME), shell
(SHELL), user name (USER and LOGNAME), and a default path (PATH)
•Change to our user ID ( setuid)and invoke our login shell, as in
execl("/bin/sh", "-sh", (char *)0);
Network Logins
the connection between the terminal and the computer isn’t point-to-point. In this case, loginis simply a service
available, just like any other network service, such as FTP or SMTP.
A single process waits for most network connections: the inetd process, sometimes called the Internet superserver.
As part of the system start-up,init invokes a shell that executes the shell script /etc/rc.One of the daemons that is started by this shell script is inetd.Once the shell script terminates, the parent process of inetd becomes init; inetd waits for TCP/IP connection requests to arrive at the host.
The telnetd process then opens a pseudo terminal device and splits into two processes using fork.The parent handles the communication across the network connection, and the child does an exec of the login program
Process Groups
In addition to having a process ID, each process belongs to a process group.
Each process group has a unique process group ID.
#include <unistd.h>
pid_t getpgrp(void);
Returns: process group ID of calling process
#include <unistd.h>
pid_t getpgid(pid_t pid);
Returns: process group ID if OK, −1 on error
If pid is 0, the process group ID of the calling process is returned. Thus
getpgid(0);
is equivalent to
getpgrp();
特别注意这两个API的名称。。。被坑10分钟debug
Each process group can have a process group leader.The leader is identified by its process group ID being equal to its process ID.
The process group still exists, as long as at least one process is in the group, regardless of whether the group leader terminates. This is called the process group lifetime—the period of time that begins when the group is created and ends when the last remaining process leaves the group.
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t temp = 0;
//test pid_t getpgrp(void);
printf("The group ID of current process : %d\n",getpgrp());
//test pid_t getpgrp(pid_t pid);
printf("The group ID of current process : %d\n",getpgid(getpid()));
return 0;
}
test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_9$ ./a.out
The group ID of current process : 20071
The group ID of current process : 20071
先印证一下fork出来的child process是parent process的group 成员
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid = 0;
printf("Current process ID:%d\n",getpid());
if((pid = fork()) < 0)
{
printf("fork error\n");
}
else if(pid == 0)
{
printf("Current process group of child process ID: %d\n",getpgrp());
exit(0);
}
else if(pid > 0)
{
waitpid(pid,NULL,0);
printf("Current process group of child process ID : %d\n",getpgrp());
}
return 0;
}
test result:
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_9$ ./a.out
Current process ID:20128
Current process group of child process ID: 20128
Current process group of parent process ID : 20128
A process joins an existing process group or creates a new process group by calling setpgid
#include <unistd.h>
int setpgid(pid_t pid,pid_t pgid );
Returns: 0 if OK,−1 on error
This function sets the process group ID to pgid in the process whose process ID equals pid.If the two arguments are equal, the process specified by pid becomes a process group leader .If pid is 0, the process ID of the caller is used. Also, if pgid is 0, the process ID specified by pid is used as the process group ID。
Furthermore, it can’t change the process group ID of one of its children after that child has called one of the exec functions.
Sessions
A session is a collection of one or more process groups。
A process establishes a new session by calling the setsid function.
#include <unistd.h>
pid_t setsid(void);
Returns: process group ID if OK, −1 on error
If the calling process is not a process group leader ,t his function creates a new session.
也就是说sessions leader 和group leader不可能是同一个ID
Three things happen.
1. The process becomes the session leader of this new session. (A session leader is
the process that creates a session.) The process is the only process in this new
session.
2. The process becomes the process group leader of a new process group. The new
process group ID is the process ID of the calling process.
3. The process has no controlling terminal. (We’ll discuss controlling terminals in
the next section.) If the process had a controlling terminal before calling
setsid, that association is broken.
#include <unistd.h>
pid_t getsid(pid_t pid);
Returns: session leader’s process group ID if OK, −1 on error
这个demo出来的很艰难。。。。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid = 0;
printf("Current process -- parent process ID:%d\n",getpid());
printf("Current process group of parent process ID : %d\n",getpgrp());
if((pid = fork()) < 0)
{
printf("fork error\n");
}
else if(pid == 0)
{
pid_t pgrp;
printf("child process ID:%d\n",getpid());
printf("Current process group of child process ID: %d\n",getpgrp());
printf("after set new group\n");
setsid();
printf("Current process group of child process ID: %d\n",getpgrp());
printf("Current sessions ID :%d\n",getsid(getpid()));
exit(0);
}
else if(pid > 0)
{
waitpid(pid,NULL,0);
printf("Current process group of parent process ID : %d\n",getpgrp());
printf("Current session ID of parent process : %d\n",getsid(getpid()));
}
return 0;
}
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_9$ ./a.out
Current process -- parent process ID:20833
Current process group of parent process ID : 20833
child process ID:20834
Current process group of child process ID: 20833
after set new group
Current process group of child process ID: 20834
Current sessions ID :20834
Current process group of parent process ID : 20833
Current session ID of parent process : 20635
If pid is 0, getsid re turns the process group ID of the calling process’s session leader.
For security reasons, some implementations may restrict the calling process from obtaining the process group ID of the session leader if pid doesn’t belong to the same session as the caller.
Controlling Terminal
•A session can have a single controlling terminal.This is usually the terminal
device (in the case of a terminal login) or pseudo terminal device (in the case of a
network login) on which we log in.
•The session leader that establishes the connection to the controlling terminal is
called the controlling process.
•The process groups within a session can be divided into a single foreground
process group and one or more background process groups .
•If a session has a controlling terminal, it has a single foreground process group
and all other process groups in the session are background process groups.这就意味着有且只有一个foreground group,允许多个background group。
•Whenever we press the terminal’s interrupt key (often DELETE or Control-
C),
the interrupt signal is sent to all processes in the foreground process group.
•Whenever we press the terminal’s quit key (often Control-backslash), the quit
signal is sent to all processes in the foreground process group.
•If a modem (or network) disconnect is detected by the terminal interface, the
hang-up signal is sent to the controlling process (the session leader).
我之前有个疑问,会不会有下图这种情况出现呢?
交集,可能吗?我一开始也比较纠结,APUE居然没有给出说明。理性的分析就能得出结论。Impossible!
我们是可以调用getpgrp()的,返回组ID,这个时候proc2和proc3返回什么值呢? 他不可能返回两个不同的组ID
于是完全可以得出结论,group process 和group process 之间不可能出现交集进程!
The way a program guarantees that it is talking to the controlling terminal is to open the file /dev/tty
Naturally,
if the program doesn’t have a controlling terminal, the open of this device will fail.
tcgetpgrp, tcsetpgrp,and tcgetsid Functions
#include <unistd.h>
pid_t tcgetpgrp(int fd );
Returns: process group ID of foreground process group if OK,−1 on error
int tcsetpgrp(int fd ,pid_t pgrpid );
Returns: 0 if OK,−1 on error
#include <termios.h>
pid_t tcgetsid(int fd );
Returns: session leader’s process group ID if OK, −1 on error
注意filedecriptor必须是和terminal相关联的文件,例如/dev/tty , 否则返回-1
#include <stdio.h>
#include <unistd.h>
int main()
{
int file_descriptor = 0;
if((file_descriptor = open("/dev/tty","r")) < 0)
{
printf("open error\n");
}
printf("foreground ID:%d\n",tcgetpgrp(file_descriptor));
close(file_descriptor);
return 0;
}
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_9$ ./a.out
foreground ID:20905
Most applications don’t call these two functions directly.Instead, the functions are
normally called by job-control shells.
Job Control
很多人对job control的必要性有争议,但是没办法这好似POXIS的明文规定,要支持这家伙
Orphaned Process Groups
就是前面讲的zombie。。。。
We’ve mentioned that a process whose parent terminates is called an orphan and is inherited by the init process.
#include"apue.h"
#include"myerr.h"
#include"errno.h"
#include"stdio.h"
static void
sig_hup(int signo)
{
printf("SIGHUP received,pid = %d\n",getpid());
}
static void
pr_ids(char* name)
{
printf("%s: pid = %d,ppid = %d,pgrp = %d,tppgrp = %d,\n",name,getpid(),getppid(),getpgrp(),tcgetpgrp(STDIN_FILENO));
fflush(stdout);
}
int main(int argc,char* argv[])
{
char c;
pid_t pid;
pr_ids("parent");
if((pid = fork()) < 0)
{
err_sys("fork error\n");
}
else if(pid > 0)
{
sleep(5);
exit(0);
}
else
{
pr_ids("child");
signal(SIGHUP,sig_hup);
kill(getpid(),SIGTSTP);
pr_ids("child");
if(read(STDIN_FILENO,&c,1) != 1)
{
printf("read error from controlling TTY,errno = %d\n",errno);
}
exit(0);
}
}
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_9$ ./a.out
parent: pid = 20928,ppid = 20635,pgrp = 20928,tppgrp = 20928,
child: pid = 20929,ppid = 20928,pgrp = 20928,tppgrp = 20928,
SIGHUP received,pid = 20929
child: pid = 20929,ppid = 1,pgrp = 20928,tppgrp = 20635,
read error from controlling TTY,errno = 5