信号

信号是软件中断的模拟,可以在任何时候发给进程,如果进程处于未执行状态,该信号就由内核保存,直到进程恢复执行再传递给它。
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