Linux的屏蔽子进程详细信息,LINUX编程学习笔记(十五) 进程控制 文件锁 信号处理与屏蔽...

一 进程的基本控制

1 进程的常见控制函数

1.1 为什么需要控制进程?

1.2 休眠 pause/sleep/usleep

1.3 on_exit atexit

int atexit(void (*function)(void));

int on_exit(void (*function)(int , void *), void *arg);

注册一个函数,在调用exit或者main函数return前调用。

atexit的函数较简单,无参数

on_exit的函数函数中int是exit或者rerurn的返回值(1字节的 不需要宏解析),void*是后面的arg

可以注册多次,但只调用一次。

多进程中子进程会复制该注册,但只要运行一次其他的都被取消。

#include

#include

void func()

{

printf("before over\n");

}

int main()

{

printf("Process\n");

atexit(func);

return 0;

}

#include

#include

void func(int statue,void *args)

{

printf("before over\n");

printf("statue = %d\n",statue);

printf("args = %s\n",(char *)args);

}

int main()

{

printf("Process\n");

char *str = "hello";

on_exit(func,(void *)str);

return 99;

}

zhao@ubuntu:~/unix/6$ ./a.out

Process

before over

statue = 99

args = hello

2 进程与文件锁

在多进程下文件读写是共享的

问题:怎么知道一个文件正在被另外进程读写?

解决方案: 文件锁(默认是建议锁 强制锁需重编译内核)

API:

fcntl (文件锁受内核参数影响)

编程技巧:

对文件加锁

判定一个文件是否存在锁

函数说明:

int fcntl(

int fd, //被加锁的文件描述符

int cmd, //锁的操作方式: F_SETLK (若已加锁,异常返回) F_SETLKW(若已加锁则等待) F_UNLK(解锁)

struct flock *fk //锁的描述

);

struct flock {

...

short l_type;    /* Type of lock: F_RDLCK,

F_WRLCK, F_UNLCK */

short l_whence;  /* How to interpret l_start:

SEEK_SET, SEEK_CUR, SEEK_END */

off_t l_start;   /* Starting offset for lock */

off_t l_len;     /* Number of bytes to lock */

pid_t l_pid;     /* PID of process blocking our lock

(F_GETLK only) */

...

};

加锁

#include

#include

#include

#include

int main()

{

int fd;

struct flock lk={0};

//打开一个文件

fd = open("test",O_RDWR|O_CREAT,0666);

if(fd == -1)

{

printf("::%m\n"),exit(-1);

}

//描述锁

lk.l_type = F_WRLCK;

lk.l_whence=SEEK_SET;

lk.l_start=5;

lk.l_len=10;

//加锁

int r = fcntl(fd,F_SETLK,&lk);

if(r == 0) printf("加锁成功\n");

else

{

printf("加锁失败\n");

}

while(1);//程序结束自动解锁

}

注意这里只是对5 到 10加了锁 当加锁区域不一样时 还可以成功加上另外的锁

如果区域重叠 就会导致加锁失败

读锁

#include

#include

#include

#include

int main()

{

int fd;

struct flock lk={0}; //要是不初始化 就不能成功得到锁

fd = open("test",O_RDWR);

if(fd <0)

{

printf("::%m\n"),exit(-1);

}

int r = fcntl(fd,F_GETLK,&lk); //F_GETLK

if(r == 0)

{

printf("得到锁成功\n");

}

else

{

printf("得到锁失败\n");

}

if(lk.l_type == F_WRLCK)

{

printf("写锁\n");

}

printf("start:%d,len %d\n",lk.l_start,lk.l_len);

}

解锁

#include

#include

#include

#include

int main()

{

int fd;

struct flock lk={0};

//打开一个文件

fd = open("test",O_RDWR|O_CREAT,0666);

if(fd == -1)

{

printf("::%m\n"),exit(-1);

}

//描述锁

lk.l_type = F_WRLCK;

lk.l_whence=SEEK_SET;

lk.l_start=20;

lk.l_len=10;

//加锁

int r = fcntl(fd,F_SETLK,&lk);

if(r == 0) printf("加锁成功\n");

else

{

printf("加锁失败\n");

}

sleep(3);

/* 。。。对文件进行操作。。。*/

lk.l_type = F_UNLCK;

r = fcntl(fd,F_SETLK,&lk);

if(r == 0) printf("解锁成功\n");

else

{

printf("解锁失败\n");

}

}

注意 这些都只是建议锁,即使你读取到有锁,或者加锁失败,你都还可以对文件进行操作

二 信号

1 信号的作用

通知其他进程相应。进程间的一种通信机制。

接收信号的进程会马上停止,执行信号处理函数。(软中断)

1.1进程之中,默认的信号处理

#include

#include

#include

int main()

{

while(1)

{

printf("进程在执行\n");

sleep(1);

}

}

怎么发信号: kill -s 信号 进程id

kill -s 1 7038 //向进程7038中 发送信号1

kill  -s SIGUP 7038  //使用信号的宏

kill -l 查看所有信号

进程在执行

进程在执行

进程在执行

进程在执行

进程在执行

挂起

ctrl+c 相当与发送信号2 SIGINT 中断信号

1.2进程之中,用户的信号处理 一个进程只能绑定一个函数

一个函数可以绑定在多个进程上

#include

#include

#include

void handle(int s)

{

printf("我是信号%d\n",s);

sleep(2);

}//函数返回后 主进程才继续

int main()

{

signal(SIGINT,handle); //注册信号处理函数

while(1)

{

printf("进程在执行\n");

sleep(1);

}

}

进程在执行

^C我是信号2

进程在执行

^C我是信号2

进程在执行

^C我是信号2

进程在执行

进程在执行

进程在执行

发现ctrl+c 发送的是信号2

发现关不掉 kill -s 9

注意:信号SIGKILL SIGSTOP 不能被处理

2 信号的发送与安装、        int kill(pid_t pid, int sig);//向进程pid发送sig信号

进程ID:

>0 发送信号到指定进程

=0 发送信号到该进程所在进程组的所有进程

-1  发送所有进程  不包含init

#include

#include

#include

#include

int main()

{

while(1)

{

kill(7323,SIGINT);

sleep(5);

}

}

3 信号的应用

3.1 延时器

SIGALRM

信号发出函数 alarm

3.2 定时器

int setitimer(int which, //计时方式 ITIMER_REAL  ITMER_VIRTUAL ITIMER_PRO

const struct itimerval *val, // 定时器的时间参数

struct itimer *oldval); // 返回原来设置的定时器

ITIMER_REAL: 以系统真实的时间来计算,它送出SIGALRM信号。(常用)

ITIMER_VIRTUAL: -以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。

ITIMER_PROF: 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号。

struct itimerval {

struct timeval it_interval;  /* next value */  间隔时间

struct timeval it_value;    /* current value */ 延时时间

};

struct timeval {

long tv_sec;                /* seconds */

long tv_usec;               /* microseconds */

};

oldval 可以设置为null 不接收

#include

#include

#include

#include

void deal(int s)

{

printf("起床!\n");

}

int main()

{

signal(SIGALRM,deal);

struct itimerval v={0}; //创立一个结构体 结构体又有两个结构体

v.it_interval.tv_sec = 3;

v.it_interval.tv_usec = 1;

v.it_value.tv_sec =5;

v.it_value.tv_usec = 0;

//5秒后 每隔3秒1微秒 发出SIGALRM信号

setitimer(ITIMER_REAL,&v,NULL);

while(1)

{

}

}

4 信号的可靠与不可靠以及信号的含义

信号有丢失(信号压缩)

由于历史的缘故:信号有压缩的需求

可靠信号(实时信号)与不可靠信号(非实时信号)

早期信号 1-31 31个信号都是不可靠(与系统有关,系统会产生)。

最早31个中留了SIGUSR1 SIGUSR2给用户使用

后期信号 34-64 31个信号 ,可靠信号(用户信号)

跟系统打交道,使用早期信号

处理用户产生的信号,使用可靠信号

5 信号的操作

1 信号屏蔽

int sigprocmask(int how, //操作方式:SIG_BLOCK屏蔽   SIG_UNBLOCK解除屏蔽  SIG_SETMASK修改屏蔽

const sigset_t *set,//  操作的信号集合

sigset_t *oldset);   // 返回原来的被屏蔽的集合

编程步骤

1 声明信号集合

sigset_t sigs;

2 加入屏蔽信号

信号结合屏蔽函数

清空集合 sigemptyset

添加信号到集合 sigaddset

从集合删除信号 sigdelset

将所有信号加进去 sigfillset

判断信号是否在集合中 sigismember

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);

查询正在排队的信号(已收到 但被sigprocmask屏蔽的信号)

int sigpending(sigset_t *set);

sigpending()  returns  the set of signals that are pending for delivery

to the calling thread (i.e., the signals which have been  raised  while

blocked).  The mask of pending signals is returned in set.

#include

#include

int main()

{

sigset_t sigs; //屏蔽集合

sigset_t sig;

sigemptyset(&sigs);//清空集合

sigemptyset(&sig);//清空集合

sigaddset(&sigs,SIGINT);//

sigprocmask(SIG_BLOCK,&sigs,0); //开始屏蔽

int sum=0;

int i;

for(i=1;i<=10;i++)

{

sum+=i;

sleep(1);

sigpending(&sig);//查询正在排队(被屏蔽)的信号

if(sigismember(&sig,SIGINT)){ printf("SIGINT正在排队!\n");}

}

printf("sum=%d\n",sum);

sigprocmask(SIG_UNBLOCK,&sigs,0); //解除屏蔽

printf("over!\n");

}

2 信号屏蔽的切换

sigsuspend 信号屏蔽切换

int sigsuspend(const sigset_t *mask);

sigsuspend 暂时取代目前的屏蔽信号,然后挂起进程,直到一个信号,调用一个信号处理程序或终止该进程。

1如果该信号终止该过程sigsuspend不会返回。

2 如果信号被捕获,处理完信号处理函数首,sigsuspend()会返回,且恢复以前的屏蔽信号。

SIGKILL或SIGSTOP无法屏蔽

这个函数的主要作用是将中断限制在一定的范围内。比如signal产生的中断可能发生在任何地方

#include

#include

void handle(int s)

{

printf("SIGINT处理中\n");

}

int main()

{

signal(SIGINT,handle);

sigset_t sigp,sigq,sigs;

sigemptyset(&sigp);

sigemptyset(&sigq);

sigemptyset(&sigs);

sigaddset(&sigp,SIGINT);

sigprocmask(SIG_BLOCK,&sigp,0);

printf("屏蔽SIGINT开始\n");

int i = 0,sum=0;

for(;i<10;i++)

{

sum += i;

sleep(1);

sigpending(&sigq);//得到排队中的信号

if(sigismember(&sigq,SIGINT))

{

printf("SIGINT正在排队\n");

sigsuspend(&sigs);//只能在这里产生SIGINT的中断

printf("SIGINT处理结束\n");

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值