1、信号
(1)概念
1. 简单 2. 不能携带大量信息 3. 满足某个特设条件才发送。
(2)机制
A给B发送信号,B收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停运行,去处理信号,处理完毕再继续执行。与硬件中断类似——异步模式。但信号是软件层面上实现的中断,早期常被称为“软中断”。
信号的特质:由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。
每个进程收到的所有信号,都是由内核负责发送的,内核处理。
(3)产生信号方法
1| 按键产生,如:Ctrl+c、Ctrl+z、Ctrl+\
2| 系统调用产生,如:kill、raise、abort
3| 软件条件产生,如:定时器alarm
4| 硬件异常产生,如:非法访问内存(段错误)、除0(浮点数例外)、内存对齐出错(总线错误)
5| 命令产生,如:kill命令
6| 递达和未决的概念
递达:递送并且到达进程。
未决:产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态。
7| 阻塞信号集(信号屏蔽字)和未决信号集的概念
阻塞信号集(信号屏蔽字):将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)
未决信号集:1. 信号产生,未决信号集中描述该信号的位立刻翻转为1,表信号处于未决状态。当信号被处理对应位翻转回为0。这一时刻往往非常短暂。
2. 信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。
8| 阻塞信号影响未决信号
(4)信号的处理方式
1| 执行默认动作
<1> Term:终止进程
<2> Ign: 忽略信号 (默认即时对该种信号忽略操作)
<3> Core:终止进程,生成Core文件。(查验进程死亡原因, 用于gdb调试)
<4> Stop:停止(暂停)进程
<5> Cont:继续运行进程
2| 忽略(丢弃)
3| 捕捉(调用户处理函数)
(5)信号的编号
使用kill -l 的命令来查看,例如( 9) SIGKILL)
(6)信号的四要素
1. 编号 2. 名称 3. 事件 4. 默认处理动作
强调:9) SIGKILL 和19) SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将其设置为阻塞。
(7)Linux常规信号一览表
1) SIGHUP: 当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
2) SIGINT:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动
作为终止进程。
3) SIGQUIT:当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信
号。默认动作为终止进程。
4) SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
5) SIGTRAP:该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。
6) SIGABRT: 调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
7) SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。
8) SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。
9) SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
10) SIGUSE1:用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
11) SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。
12) SIGUSR2:另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
13) SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
14) SIGALRM: 定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程。
15) SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
16) SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。
17) SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
18) SIGCONT:如果进程已停止,则使其继续运行。默认动作为继续/忽略。
19) SIGSTOP:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。
20) SIGTSTP:停止终端交互进程的运行。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。
21) SIGTTIN:后台进程读终端控制台。默认动作为暂停进程。
22) SIGTTOU: 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。
23) SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。
24) SIGXCPU:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。
25) SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。
26) SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。
27) SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。
28) SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。
29) SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
30) SIGPWR:关机。默认动作为终止进程。
31) SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。
34) SIGRTMIN ~ (64) SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。
2、信号的产生
(1)终端按键产生信号
Ctrl + c → 2) SIGINT(终止/中断) "INT" ----Interrupt
Ctrl + z → 20) SIGTSTP(暂停/停止) "T" ----Terminal 终端。
Ctrl + \ → 3) SIGQUIT(退出)
(2)硬件异常产生信号
除0操作 → 8) SIGFPE (浮点数例外) "F" -----float 浮点数。
非法访问内存 → 11) SIGSEGV (段错误)
总线错误 → 7) SIGBUS
(3)kill函数/命令产生信号
1| 函数原型:int kill(pid_t pid, int sig);
2| 返回值:成功:0;失败:-1
3| 参数pid
<1> pid > 0: 发送信号给指定的进程。
<2> pid = 0: 发送信号给 与调用kill函数进程属于同一进程组的所有进程。
<3> pid < 0: 取|pid|发给对应进程组。
<4> pid = -1:发送给进程有权限发送的系统中所有进程。
#include
#include
#include
#include
#include
#include
#include
#define N 5
int main()
{
int i;
pid_t pid,q;
for(i = 0;i < N;i++)
{
pid = fork();
if(pid < 0)
{
perror("fork error:");
exit(1);
}
else if(pid == 0)
{
break;
}
if(i==2)
{
q = pid;
}
}
if(i < 5)
{
while(1)
{
sleep(1);
printf("I am child %d\tgetpid = %u\n",i+1,getpid());
}
}
if(i == 5)
{
kill(q,SIGKILL);
sleep(2);
printf("I am parent\tgetppid = %u\n",getppid());
while(1);
}
return 0;
}
(4)raise和abort函数
1| raise函数
给当前进程发送指定信号(自己给自己发) raise(signo) == kill(getpid(), signo);
int raise(int sig); 成功:0,失败非0值
2| abort函数
给自己发送异常终止信号 6) SIGABRT 信号,终止并产生core文件
void abort(void); 该函数无返回
3、软件条件产生信号
(1)alarm函数
1| 函数作用
设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止。
每个进程都有且只有唯一个定时器。
2| 函数原型
unsigned int alarm(unsigned int seconds);
3| 返回值
返回0或剩余的秒数,无失败。
常用:取消定时器alarm(0),返回旧闹钟余下秒数。
例:alarm(5) → 3sec → alarm(4) → 5sec → alarm(5) → alarm(0)
4| 定时,与进程状态无关(自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵尸...无论进程处于何种状态,alarm都计时。
5| 实际执行时间 = 系统时间 + 用户时间 + 等待时间
(2)setitimer函数
1| 函数作用
设置定时器(闹钟)。 可代替alarm函数。精度微秒us,可以实现周期定时。
2| 函数原型
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
3| 返回值:
成功:0;失败:-1,设置errno
4| 参数:which:指定定时方式
① 自然定时:ITIMER_REAL → 14)SIGLARM 计算自然时间
② 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM 只计算进程占用cpu的时间
③ 运行时计时(用户+内核):ITIMER_PROF → 27)SIGPROF 计算占用cpu及执行系统调用的时间
5| 提示: it_interval:用来设定两次定时任务之间间隔的时间。
it_value:定时的时长
两个参数都设置为0,即清0操作。
#include
#include
#include
unsigned int my_alarm(unsigned int sec)
{
struct itimerval it,oldit;
int ret;
it.it_value.tv_sec = sec;
it.it_value.tv_usec = 0;
it.it_interval.tv_sec = 0;
it.it_interval.tv_sec = 0;
ret = setitimer(a);
if(ret == -1)
{
perror("setitimer error:");
exit(1);
}
return oldit.it_value.tv_sec;
}
int main()
{
int i;
my_alarm(1);
for(i = 0;;i++)
{
printf("%d\n",i);
}
return 0;
}
4、signal捕捉信号
(1)signal函数原型
typedef void (*sighandler_t)(int); //自定义函数,如果成功捕捉信号,则执行该函数
sighandler_t signal(int signum, sighandler_t handler);
(2)实现代码
#include
#include
#include
#include
void myfunc(int signo)
{
printf("hello world");
}
int main()
{
struct itimerval it,oldit;
signal(SIGALRM,myfunc);
it.it_value.tv_sec = 5;
it.it_value.tv_usec = 0;
it.it_interval.tv_sec = 3;
it.it_interval.tv_usec = 0;
if(setitimer(ITIMER_REAL,&it,oldit)==-1)
{
perror("setitimer error:");
return -1;
}
}
5、信号集设定
(1)常见函数(sigset_t类型 例如:sigset_t myset)
int sigemptyset(sigset_t *set); 将某个信号集清0 成功:0;失败:-1
int sigfillset(sigset_t *set); 将某个信号集置1 成功:0;失败:-1
int sigaddset(sigset_t *set, int signum); 将某个信号加入信号集 成功:0;失败:-1
int sigdelset(sigset_t *set, int signum); 将某个信号清出信号集 成功:0;失败:-1
int sigismember(const sigset_t *set, int signum);判断某个信号是否在信号集中 返回值:在集合:1;不在:0;出错:-1
sigset_t类型的本质是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。
(2)sigprocmask函数
1| 函数原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
2| 作用:用来屏蔽信号、解除屏蔽也使用该函数。
3| 参数
set:传入参数,是一个位图,set中哪位置1,就表示当前进程屏蔽哪个信号。(自定义的信号集)
oldset:传出参数,保存旧的信号屏蔽集。(阻塞信号集)
how参数取值: 假设当前的信号屏蔽字为mask
1.SIG_BLOCK: 当how设置为此值,set表示需要屏蔽的信号。相当于 mask = mask|set
2.SIG_UNBLOCK: 当how设置为此,set表示需要解除屏蔽的信号。相当于 mask = mask & ~set
3.SIG_SETMASK: 当how设置为此,set表示用于替代原始屏蔽及的新屏蔽集。相当于 mask = set若,调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。
4| 返回值:成功:0;失败:-1,设置errno
(3)sigpending函数
1| 函数原型:int sigpending(sigset_t *set);
2| 作用:读取当前进程的未决信号集
3| 返回值:成功:0;失败:-1,设置errno
#include
#include
void printped(sigset_t *ped)
{
int i;
for(i=1;i<32;i++)
{
if(sigismember(ped,i)==1)
{
putchar('1');
}
else
{
putchar('0');
}
}
}
int mian()
{
sigset_t myset,oldset,ped;
sigemptyset(&mtset);
sigaddset(&myset,SIGQUIT);
sigprocmask(SIG_BLOCK,&myset,&oldset);
while(1)
{
sigpending(&ped);
printped(&ped);
sleep(1);
}
return 0;
}
6、sigaction函数
1| 函数原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
2| 作用:修改信号处理动作(通常在Linux用其来注册一个信号的捕捉函数)
3| 返回值:成功:0;失败:-1,设置errno
4| 参数
act:传入参数,新的处理方式。
oldact:传出参数,旧的处理方式。 (可以用NULL代替)
5|
struct sigaction结构体
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sa_restorer:该元素是过时的,不应该使用,POSIX.1标准将不指定该元素。(弃用)
sa_sigaction:当sa_flags被指定为SA_SIGINFO标志时,使用该信号处理程序。(很少使用)
重点掌握:
① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为SIG_IGN表忽略 或 SIG_DFL表执行默认动作
② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
③ sa_flags:通常设置为0,表使用默认属性。
#include
#include
#include
#include
void docatch(int signo)
{
printf("%d signal is catched\n",signo);
}
int main()
{
int ret;
struct sigaction act;
act.sa_handler = docatch;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT);
act.sa_flags = 0;
ret = sigaction(SIGINT,&act,NULL);
if(ret < 0)
{
perror("sigaction error:");
exit(1);
}
while(1);
return 0;
}
7、pause函数
(1)函数作用
调用该函数可以造成进程主动挂起,等待信号唤醒。调用该系统调用的进程将处于阻塞状态(主动放弃cpu) 直到有信号递达将其唤醒。
(2)函数原型
int pause(void);
(3)返回值
返回值:-1 并设置errno为EINTR
<1> 如果信号的默认处理动作是终止进程,则进程终止,pause函数么有机会返回。
<2> 如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause函数不返回。
<3> 如果信号的处理动作是捕捉,则【调用完信号处理函数之后,pause返回-1】
<4> errno设置为EINTR,表示“被信号中断”。想想我们还有哪个函数只有出错返回值。
<5> pause收到的信号不能被屏蔽,如果被屏蔽,那么pause就不能被唤醒。
#include
#include
#include
#include
#include
void catch_sigalrm(int signo)
{
;
}
unsigned int mysleep(unsigned int seconds)
{
int ret;
struct sigaction act oldact;
act.sa_handler = catch_sigalrm;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
ret = sigaction(SIGALRM,&act,&oldact);
if(ret == -1)
{
perror("sigaction error:");
exit(1);
}
alarm(seconds);
ret = pause()
if(ret == -1 && errno == EINTR)
{
printf("pause sucess\n");
}
ret = alarm(0);
sigaction(SIGALRM,&oldact,NULL);
return ret;
}
int main()
{
while(1)
{
mysleep(3);
printf("************\n");
}
return 0;
}
8、时序竞态
#include
#include
#include
#include
#include
void catch_sigalrm(int signo)
{
;
}
unsigned int mysleep(unsigned int seconds)
{
struct sigaction act,oldact;
sigset_t newmask,oldmask,suspmask;
unsigned int unslept;
act.sa_handler = catch_sigalrm;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM,&act,&oldact);
sigemptyset(&newmask);
sigaddset(&newmask,SIGALRM);
sigprocmask(SIG_BLOCK,&newmask,&oldmask);
alarm(seconds);
suspmask = oldmask;
sigdelset(&suspmask,SIGALRM);
sigsuspend(&suspmask);
unslept = alarm(0);
sigaction(SIGALRM,&oldact,NULL);
sigprocmask(SIG_SETMASK,&oldmask,NULL);
return unslept;
}
int main()
{
while(1)
{
mysleep(3);
printf("************\n");
}
return 0;
}
9、全局变量异步I/O
#include
#include
#include
#include
int n = 0, flag = 0;
void sys_err(char *str)
{
perror(str);
exit(1);
}
void do_sig_child(int num)
{
printf("I am child %d\t%d\n", getpid(), n);
n += 2;
flag = 1;
sleep(1);
}
void do_sig_parent(int num)
{
printf("I am parent %d\t%d\n", getpid(), n);
n += 2;
flag = 1;
sleep(1);
}
int main(void)
{
pid_t pid;
struct sigaction act;
if ((pid = fork()) < 0)
sys_err("fork");
else if (pid > 0) {
n = 1;
sleep(1);
act.sa_handler = do_sig_parent;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGUSR2, &act, NULL);
do_sig_parent(0);
while (1) {
;
if (flag == 1) {
kill(pid, SIGUSR1);
flag = 0;
}
}
} else if (pid == 0) {
n = 2;
act.sa_handler = do_sig_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGUSR1, &act, NULL);
while (1) {
;
if (flag == 1) {
kill(getppid(), SIGUSR2);
flag = 0;
}
}
}
return 0;
}
示例中,通过flag变量标记程序实行进度。flag置1表示数数完成。flag置0表示给对方发送信号完成。
10、可重入函数和不可重入函数
一个函数在被调用执行期间(尚未调用结束),由于某种时序又被重复调用,称之为“重入”。
注意事项
1.定义可重入函数,函数内不能含有全局变量及static变量,不能使用malloc、free
2.信号捕捉函数应设计为可重入函数
3.信号处理程序可以调用的可重入函数可参阅man 7 signal
4.没有包含在上述列表中的函数大多是不可重入的,其原因为:
a)使用静态数据结构
b)调用了malloc或free
c)是标准I/O函数
11、SIGCHLD信号
(1)产生条件
1| 子进程终止时
2| 子进程接收到SIGSTOP信号停止时
3| 子进程处在停止态,接受到SIGCONT后唤醒时
(2)借助SIGCHLD信号回收子进程
#include
#include
#include
#include
#include
void sys_err(char *str)
{
perror(str);
exit(1);
}
void do_sig_child(int signo)
{
int status; pid_t pid;
while ((pid = waitpid(0, &status, WNOHANG)) > 0) {
if (WIFEXITED(status))
printf("child %d exit %d\n", pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("child %d cancel signal %d\n", pid, WTERMSIG(status));
}
}
int main(void)
{
pid_t pid; int i;
for (i = 0; i < 10; i++) {
if ((pid = fork()) == 0)
break;
else if (pid < 0)
sys_err("fork");
}
if (pid == 0) {
int n = 1;
while (n--) {
printf("child ID %d\n", getpid());
sleep(1);
}
return i+1;
} else if (pid > 0) {
struct sigaction act;
act.sa_handler = do_sig_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
while (1) {
printf("Parent ID %d\n", getpid());
sleep(1);
}
}
return 0;
}
(3)SIGCHLD信号注意问题
1| 子进程继承了父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集spending
2| 注意注册信号捕捉函数的位置
3| 应该在fork之前,阻塞SIGCHLD信号。注册完捕捉函数后解除阻塞。
12、中断系统调用
(1)慢速系统调用
可能会使进程永远阻塞的一类。如果在阻塞期间收到一个信号,该系统调用就被中断,不再继续执行(早期);也可以设定系统调用是否重启。如,read、write、pause、wait...
(2)其他系统调用
getpid、getppid、fork...
(3)慢速系统调用被中断的相关行为,实际上就是pause的行为: 如,read
13、终端(输入设备和输出设备)
(1)相关操作
1| Alt + Ctrl + F1、F2、F3、F4、F5、F6 字符终端
2| pts (pseudo terminal slave) 指伪终端。
3| Alt + F7 图形终端
4| SSH、Telnet... 网络终端
(2)ttyname函数
1| 函数原型:char *ttyname(int fd);
2| 作用:由文件描述符查出对应的文件名
3| 返回值:成功:终端名;失败:NULL,设置errno
通过实验看一下各种不同的终端所对应的设备文件名。
int main(void)
{
printf("fd 0: %s\n", ttyname(0));
printf("fd 1: %s\n", ttyname(1));
printf("fd 2: %s\n", ttyname(2));
return 0;
}
14、进程组
(1)注意点
1| 当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其进程ID
2| 可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死。
3| 组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
4| 进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
5| 一个进程可以为自己或子进程设置进程组ID
(2)进程组操作函数
1| getpgrp函数(获取当前进程的进程组ID)
<1> 函数原型:pid_t getpgrp(void);
<2> 返回值:总是返回调用者的进程组ID
2| getpgid函数(获取指定进程的进程组ID)
<1> 函数原型:pid_t getpgid(pid_t pid);
<2> 返回值:成功:0;失败:-1,设置errno
如果pid = 0,那么该函数作用和getpgrp一样。
3| setpgid函数(改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。)
<1> 函数原型:int setpgid(pid_t pid, pid_t pgid);
<2> 返回值:成功:0;失败:-1,设置errno
将参1对应的进程,加入参2对应的进程组中。
4| 注意:
1. 如改变子进程为新的组,应fork后,exec前。
2. 权级问题。非root进程只能改变自己创建的子进程,或有权限操作的进程
#include
#include
#include
int main()
{
pid_t pid;
pid = fork();
if(pid == -1)
{
perror("fork error:");
exit(1);
}
else if(pid == 0)
{
printf("child PID == %d\n",getpid());
printf("child Group ID == %d\n",getpgid(0));
sleep(7);
printf("-----Group ID of child is changed to %d\n",getpgid(0));
exit(0);
}
else if(pid > 0)
{
sleep(1);
setpgid(pid,pid);
sleep(13);
printf("\n");
printf("parent PID == %d\n",getpid());
printf("parent's parent process PID == %d\n",getppid());
printf("parent Group ID == %d\n",getpgid(0));
sleep(5);
setpgid(getpid(),getppid());
printf("\n------Group ID of parent is changed to %d\n",getpgid(0));
while(1);
}
return 0;
}
15、会话
(1)创建会话
1| 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
2| 该进程成为一个新进程组的组长进程。
3| 需有root权限(ubuntu不需要)
4| 新会话丢弃原有的控制终端,该会话没有控制终端
5| 该调用进程是组长进程,则出错返回
6| 建立新会话时,先调用fork, 父进程终止,子进程调用setsid
(2)getsid函数
1| 作用:获取进程所属的会话ID
2| 函数原型:pid_t getsid(pid_t pid);
3| 返回值: 成功:返回调用进程的会话ID;失败:-1,设置errno
4| 参数说明
<1> pid为0表示察看当前进程session ID
<2> 组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。
(3)setsid函数
1| 函数作用:创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。
2| 函数原型:pid_t setsid(void);
3| 返回值:成功:返回调用进程的会话ID;失败:-1,设置errno
4| 说明
调用了setsid函数的进程,既是新的会长,也是新的组长。
#include
#include
#include
int main()
{
pid_t pid;
pid = fork();
if(pid == -1)
{
perror("fork error:");
exit(1);
}
else if(pid == 0)
{
printf("child process PID is %d\n",getpid());
printf("Group ID of child is %d\n",getpgid(0));
printf("Session ID of child is %d\n",getsid(0));
sleep(10);
setsid();
printf("Changed:\n");
printf("child process PID is %d\n",getpid());
printf("Group ID of child is %d\n",getpgid(0));
printf("Session ID of child is %d\n",getsid(0));
sleep(20);
exit(0);
}
return 0;
}