信号
信号是软件中断的模拟,可以在任何时候发给进程,如果进程处于未执行状态,该信号就由内核保存,直到进程恢复执行再传递给它。
SIGKILL和SEGSTOP是应用程序无法捕捉和忽略的。
几个常用的快捷键和信号:
ctrl + C
—— SIGINT 中断信号
ctrl + \
—— SIGQUIT 退出信号
ctrl + Z
—— SIGTSTP 进程挂起
functions about signals:
function
| introduction
|
int kill(pid_t pid, int sig)
| send signal to a process
|
int raise(int sig)
| send a signal to the caller
|
unsigned int alarm(unsigned int seconds)
| set an alarm clock for delivery of a signal
|
int pause(void)
| wait for signal
|
typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
| ANSI C signal handling
|
int sigemptyset(sigset_t *set)
| initializes the signal set given by set to empty, with all signals excluded from the set.
|
int sigfillset(sigset_t *set)
| initializes set to full, including all signals
|
int sigaddset(sigset_t *set, int signum)
| add respectively signal signum from set
|
int sigdelset(sigset_t *set, int signum)
| delete respectively signal signum from set
|
int sigismember(const sigset_t *set, int signum)
| tests whether signum is a member of set
|
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
| examine and change blocked signals
|
下面是子进程给自己发送SIGSTOP信号,用于暂停进程。父进程中waitpid(pid,0,WNOHANG)
用于子进程没有退出立即返回(return immediately if no child has exited.)。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
pid_t pid = fork();
if(pid == 0){ // child
printf("child id is %d\n",getpid());
raise(SIGSTOP);
}
else if(pid > 0){ // father
printf("father pid is %d\n",getpid());
if(waitpid(pid,0,WNOHANG) == 0){
int ret = kill(pid,SIGKILL);
if(ret < 0) perror("ending child failed ");
else printf("father process end child process.\n");
}
}
else {
perror("fork ");
exit(1);
}
return 0;
}
/*
father pid is 6284
father process end child process.
*/
alarm用于设置多少秒后发出SIGALAR信号,时间片函数控制事件发生的时间点。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
void handler(int sig){
printf("hello world\n");
}
int main(){
int i;
alarm(3);
signal(SIGALRM,handler);
for(i=1;i<=6;i++){
printf("times is %d\n",i);
sleep(1);
}
return 0;
}
/*
times is 1
times is 2
times is 3
hello world
times is 4
times is 5
times is 6
*/
信号的阻塞:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler(){
printf("signal SIGINT has been blocked for 5 seconds, now it works.\n");
}
int main(){
signal(SIGINT,handler);
int i;
sigset_t set;
if(sigemptyset(&set) == -1){
perror("sigemptyset ");
}
if(sigaddset(&set,SIGINT) == -1){
perror("sigaddset ");
}
if(sigprocmask(SIG_BLOCK,&set,NULL) == -1){ /* make set blocked */
perror("sigpromask ");
}
raise(SIGINT);
for(i=0;i<5;i++){
printf("sleeps %d seconds...\n",i+1);
sleep(1);
}
if(sigprocmask(SIG_UNBLOCK,&set,NULL) < 0){
perror("sigprocmask ");
}
return 0;
}
/*
sleeps 1 seconds...
sleeps 2 seconds...
sleeps 3 seconds...
sleeps 4 seconds...
sleeps 5 seconds...
signal SIGINT has been blocked for 5 seconds, now it works.
*/
管道
进程通信方式中的管道是半双工的,仅用于父子进程或者兄弟进程,单独构成独立的文件系统,存在于内存之中。
相关的函数:
function
| introduction
|
int pipe(int pipefd[2])
| create pipe
|
int pipe2(int pipefd[2], int flags)
| create pipe
|
FILE *popen(const char *command, const char *type)
| opens a process by creating a pipe, forking, and invoking the shell
|
int pclose(FILE *stream)
| waits for the associated process to terminate and returns the exit status of the command as returned by wait4(2)
|
int mkfifo(const char *pathname, mode_t mode)
| make a FIFO special file (a named pipe)
|
例子:fork()创建进程,父进程给子进程通过管道发送消息“I’m father.”, 子进程读取;子进程给父进程发送消息“I’m child.”, 父进程再读取。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(){
int pipe_fd[2];
if(pipe(pipe_fd) < 0){
perror("pipe ");
exit(1);
}
int bytes;
char str[105] = {0};
int pid = fork();
if(pid < 0) perror("fork ");
else if(pid == 0){ // child
bytes = read(pipe_fd[0],str,104);
if(bytes > 0){
printf("child read from pipe: %s\n",str);
}
else perror("child read ");
//fdopen(pipe_fd[1],"w");
bytes = write(pipe_fd[1],"I'm child.",104);
if(bytes <= 0){
perror("child write ");
printf("child write %d\n",errno);
exit(1);
}
}
else {
bytes = write(pipe_fd[1],"I'm father.",104);
if(bytes <= 0){
perror("father write ");
exit(1);
}
waitpid(pid,NULL,0); // wait for child process.
bytes = read(pipe_fd[0],str,104);
if(bytes > 0){
printf("father read from pipe: %s\n",str);
}
else perror("father read ");
}
return 0;
}
/*
works:
child read from pipe: I'm father.
father read from pipe: I'm child.
if we put "waitpid(pid,NULL,0);" after "else perror("father read ");",
we would find read is blocked:
father read from pipe: I'm father.
^C
we can also get that process has individual pipe_fd[] for himself.
*/
使用管道实现ls |grep .c
程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
FILE *fp = popen("ls","r"); /* read pipe */
if(fp == NULL){
perror("popen ");
exit(1);
}
char buf[1005]={0};
if(fread(buf,1,1004,fp) <= 0){ /* store contents in buf */
perror("fread ");
exit(1);
}
pclose(fp);
fp = popen("grep -a .c","w"); /* write pipe */
if(fwrite(buf,1,1004,fp) <= 0){ /* write contents from buf to file stream fp */
perror("fwrite ");
exit(1);
}
while(fgets(buf,1004,fp) != NULL){
printf("%s\n",buf);
}
pclose(fp);
return 0;
}
/*
it's equal to shell command `ls |grep .c`
*/
popen()中的shell命令是在fork出来的子进程中执行的。
命名管道
命名管道FIFO是一种特殊的pipe,可以使用在任何两个进程中通信。
相关函数:
int mkfifo(const char *pathname, mode_t mode);
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int close(int
下面实现两个进程的命名管道通信。
(open操作管道,以读或者写的方式打开,如果没有另一个进程进行写或读,是会一直阻塞的,fifo存在自动阻塞的特性。可以使用读写的方式打开避免这样的尴尬。)
processA.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <fcntl.h>
int main(){
mkfifo("fifo1",0644);
mkfifo("fifo2",0644);
int wfd = open("fifo1",O_RDWR);
int rfd = open("fifo2",O_RDWR);
struct timeval timer = {0,0};
fd_set wfd_set, rfd_set;
if(wfd == -1 || rfd == -1){
perror("open ");
exit(1);
}
printf("process A:\n");
while(1){
FD_ZERO(&rfd_set);
FD_SET(rfd,&rfd_set);
FD_SET(fileno(stdin),&rfd_set);
if(select(rfd+1,&rfd_set,NULL,NULL,&timer) <= 0) continue;
if(FD_ISSET(rfd,&rfd_set)){
char str[105] = {0};
read(rfd,str,104);
printf("message from process B: %s\n",str);
}
if(FD_ISSET(fileno(stdin),&rfd_set)){
char str[105] = {0};
fgets(str,104,stdin);
if(write(wfd,str,strlen(str)) <= 0){
perror("write ");
exit(1);
}
}
}
close(rfd);
close(wfd);
return 0;
}
/*
process A:
hello, I'm process A
message from process B: I get it
I'm glad to heard from you.
message from process B: see you later.
*/
processB.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <fcntl.h>
int main(){
mkfifo("fifo1",0644);
mkfifo("fifo2",0644);
int rfd = open("fifo1",O_RDWR);
int wfd = open("fifo2",O_RDWR);
struct timeval timer = {0,0};
fd_set wfd_set, rfd_set;
if(wfd == -1 || rfd == -1){
perror("open ");
exit(1);
}
printf("process B:\n");
while(1){
FD_ZERO(&rfd_set);
FD_SET(rfd,&rfd_set);
FD_SET(fileno(stdin),&rfd_set);
if(select(rfd+1,&rfd_set,NULL,NULL,&timer) <= 0) continue;
if(FD_ISSET(rfd,&rfd_set)){
char str[105] = {0};
read(rfd,str,104);
printf("message from process A: %s\n",str);
}
if(FD_ISSET(fileno(stdin),&rfd_set)){
char str[105] = {0};
fgets(str,104,stdin);
if(write(wfd,str,strlen(str)) <= 0){
perror("write ");
exit(1);
}
}
}
close(rfd);
close(wfd);
return 0;
}
/*
process B:
message from process A: hello, I'm process A
I get it
message from process A: I'm glad to heard from you.
see you later.
*/
我们可以在当前目录下发现多了两个fifo文件:
$ ls -l fifo*
prw-r--r-- 1 edemon edemon 0 1月 7 16:26 fifo1
prw-r--r-- 1 edemon edemon 0 1月 7 16:26 fifo2
消息队列
消息队列是保存在内核中的消息列表。用户进程可以向消息队列中添加消息,也可以从消息队列中(自定义)读取消息。通信的进程没有关系上的 限制。
消息队列的相关函数
IPC: inter-process communication
functions
| introduction
|
key_t ftok(const char *pathname, int proj_id)
| convert a pathname and a project identifier to a System V IPC key
|
int msgget(key_t key, int msgflg)
| get a System V message queue identifier
|
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
| send messages to a System V message queue
|
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
| receive messages from a System V message queue
|
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
| System V message control operations
|
例子:使用消息队列,父进程给子进程发送消息。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main(){
key_t key = ftok(".",0);
int msg_id = msgget(key,IPC_CREAT|0644);
if(msg_id == -1){
perror("msgget ");
exit(1);
}
printf("please enter message to transform: \n");
char message[105] = {0};
fgets(message,104,stdin);
int pid = fork();
if(pid == -1){
perror("fork ");
exit(1);
}
else if(pid == 0){
if(msgsnd(msg_id,message,strlen(message),0) == -1) {
perror("msgsnd ");
exit(1);
}
}
else {
waitpid(pid,NULL,0);
memset(message,0,sizeof(message));
if(msgrcv(msg_id,message,104,0,0) <= 0){
perror("msgrcv ");
exit(1);
}
printf("father process receive message from child process:\n%s",message);
if(msgctl(msg_id,IPC_RMID,NULL) == -1){
perror("msgctl ");
exit(1);
}
}
return 0;
}
/*
please enter message to transform:
hello world
father process receive message from child process:
hello world
*/
共享内存
共享内存用于多个进程之间的通信,因为不需要数据的来回复制,所以是最快的进程之间通信的机制。
function
| introduction
|
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
| map files or devices into memory
|
int munmap(void *addr, size_t length)
| unmap files or devices into memory
|
int shmget(key_t key, size_t size, int shmflg)
| allocates a shared memory segment
|
void *shmat(int shmid, const void *shmaddr, int shmflg)
| shared memory operations
|
int shmdt(const void *shmaddr)
| detaches the shared memory segment
|
系统V共享内存的方式(The POSIX shared memory object),通过映射特殊文件系统shm中的文件实现共享内存的通信。
例子:利用系统V共享内存的通信方法,子进程给父进程发送信息。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
int main(){
int fd = shm_open("test",O_CREAT|O_RDWR,0644);
if(fd == -1){
perror("shm_open ");
exit(1);
}
ftruncate(fd,105);
char *str = (char *)mmap(NULL,105,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if((void*)-1 == str){
perror("mmap ");
exit(1);
}
int pid = fork();
if(pid == -1) perror("fork ");
else if(pid == 0){
printf("child process %d set message str.\n",getpid());
strcpy(str,"hello world, I'm child.");
exit(0);
}
else {
waitpid(pid,0,0);
printf("father process get share memory message: %s\n",str);
if(munmap(str,105)<0) perror("munmap ");
str = NULL;
}
return 0;
}
/*
shm_open, shm_unlink: Link with -lrt.
[edemon@CentOS workspace]$ gcc shm_com.c -lrt
[edemon@CentOS workspace]$ ./a.out
child process 8049 set message str.
father process get share memory message: hello world, I'm child.
we can find a new file test in /dev/shm/
[edemon@CentOS workspace]$ ls /dev/shm
pulse-shm-1359020668 pulse-shm-2129357730 pulse-shm-3531010208 test
pulse-shm-1643945986 pulse-shm-3326717209 pulse-shm-3944084079
*/
如果使用普通文件映射,那么改变open的方式即可。int fd = open("test",O_CREAT|O_RDWR|O_TRUNC,0644);
如果使用匿名映射(没有文件和内存对应),那么不使用shm_open和open函数产生文件描述符,使用mmap(): char *str = (char *)mmap(NULL,105,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
在路径/proc/sys/kernel
中存在共享内存最大字节数的限制文件shmnmax, 最大标识数的限制文件shmmni
[edemon@CentOS kernel]$ cat shmmax
4294967295
[edemon@CentOS kernel]$ cat shmmni
4096
文件系统shm安装点在交换分区上,系统V共享内存随着内核持续,系统重新引导(内核重新引导)后,所有的内存都将丢失。
[edemon@CentOS workspace]$ ls /dev/shm
pulse-shm-1812859712 pulse-shm-2430381189 pulse-shm-264722293 pulse-shm-4074297125 pulse-shm-94801651