一 进程的基本控制
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 <stdio.h> #include <stdlib.h> void func() { printf("before over\n"); } int main() { printf("Process\n"); atexit(func); return 0; }
#include <stdio.h> #include <stdlib.h> 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 <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> 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 <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> 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 <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> 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 <unistd.h> #include <stdio.h> #include <signal.h> 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 <unistd.h> #include <stdio.h> #include <signal.h> 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
<-1 发送给指定的进程组 (组ID=绝对值)
#include <unistd.h> #include <stdio.h> #include <signal.h> #include <sys/types.h> 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_PROconst 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 <signal.h> #include <stdio.h> #include <unistd.h> #include <sys/time.h> 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);
#include <stdio.h> #include <signal.h> 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()) } 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 <stdio.h> #include <signal.h> 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"); } } }
3查询被屏蔽的信号
sigpending函数返回在送往进程的时候被阻塞挂起的信号集合。这个信号集合通过参数set返回
int sigpending(sigset_t *set);
#include <stdio.h> #include <signal.h> int main() { sigset_t sigs,sigp; sigemptyset(&sigs); sigemptyset(&sigp); sigaddset(&sigs,SIGINT); sigprocmask(SIG_BLOCK,&sigs,0); int sum=0; int i; for(i=1;i<=10;i++) { sum+=i; sleep(1); sigpending(&sigp); //查询正在排队中的信号 if(sigismember(&sigp,SIGINT)) { printf("SIGINT正在排队\n"); } } printf("sum=%d\n",sum); sigprocmask(SIG_UNBLOCK,&sigs,0); printf("over!\n"); }
按下ctrl+c 后 之后每次循环中都会打印SIGINT正在排队
作者:a8887396 发表于2013-6-21 10:56:48 原文链接
阅读:22 评论:0 查看评论