Linux C 进程与进程间通信
进程常用API
- getpid 获取当前进程ID
- getppid 获取当前进程的父进程ID
- fork 创建一个进程, 子进程中返回0, 父进程中返回子进程pid.
- wait函数族 等待子进程结束, 回收子进程资源.
- wait 等待所有子进程结束
- waitpid 等待特定子进程结束(也可所有)
- exec函数族 执行外部程序, 完全替换当前进程内容, 后缀意义如下.
l: 参数为可变长参数(NULL结尾)
v: 参数为数组
p: 在当前环境变量中搜索
e: 传入环境变量再在其中搜索
- execl, execlp, execle
- execv, execvp, execve
- exit函数族 退出程序
- exit 标准库函数, 退出前进行缓冲区清理等操作.
- _exit 直接退出, 不做任何处理.
创建进程
进程结束的顺序是不确定的, 用wait来保证子进程先结束.
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
void if_error(int stat_code, char *err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
pid_t chi_pid;
int ret, chi_stat;
chi_pid = fork();
if_error(chi_pid, "fork");
if (chi_pid == 0) {
/* child process */
printf("child process running, pid: %d, ppid: %d .\n", getpid(), getppid());
sleep(2);
printf("child process finished, pid: %d, ppid: %d .\n", getpid(), getppid());
exit(0);
} else {
/* parent process */
printf("parent process running, pid: %d, ppid: %d .\n", getpid(), getppid());
ret = wait(&chi_stat);
if_error(ret, "wait");
/* wait success */
if (WIFEXITED(chi_stat)) {
printf("wait success, status code: %d .\n", WEXITSTATUS(chi_stat));
}
/* wait failed */
if (WIFSIGNALED(chi_stat)) {
printf("wait failed, status code: %d .\n", WTERMSIG(chi_stat));
}
printf("parent process finished, pid: %d, ppid: %d .\n", getpid(), getppid());
}
printf("main exit.\n");
return 0;
}
僵尸进程
僵尸进程: 已经执行结束, 但是资源还未被回收的进程.
利用system函数执行shell脚本, 发现进程状态为Z的僵尸进程.
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 80
void if_error(int stat_code, char *err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
pid_t chi_pid;
char shell_cmd[MAXLINE];
chi_pid = fork();
if_error(chi_pid, "fork");
if (chi_pid == 0) {
/* child process */
exit(0);
} else {
/* parent process */
sleep(2);
}
sprintf(shell_cmd, "ps aux | grep %d", chi_pid);
system(shell_cmd);
printf("main exit.\n");
return 0;
}
孤儿进程
孤儿进程: 父进程执行结束, 子进程还在运行的进程.(会被init进程接管)
执行结束后查看对应子进程的PPID, 是init进程.
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 80
void if_error(int stat_code, char *err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
pid_t chi_pid;
char shell_cmd[MAXLINE];
chi_pid = fork();
if_error(chi_pid, "fork");
if (chi_pid == 0) {
/* child process */
printf("before parent exit, pid: %d, ppid:%d\n", getpid(), getppid());
sleep(2);
printf("after parent exit, pid: %d, ppid:%d\n", getpid(), getppid());
sleep(2);
exit(0);
} else {
/* parent process */
printf("parent exit, child pid: %d .\n", chi_pid);
exit(0);
}
}
两次fork避免僵尸进程
第二次fork产生孙子进程, 让子进程直接结束, 孙子进程变成孤儿进程被init接管.
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
void if_error(int stat_code, char *err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
pid_t chi_pid, grand_chi_pid;
int ret;
chi_pid = fork();
if_error(chi_pid, "fork");
if (chi_pid == 0) {
/* child process */
grand_chi_pid = fork();
if_error(grand_chi_pid, "fork");
if (grand_chi_pid == 0) {
/* grand child process */
sleep(2);
printf("grand child process pid: %d, ppid: %d .\n", getpid(), getppid());
exit(0);
} else {
/* child process */
printf("child process pid: %d, ppid: %d .\n", getpid(), getppid());
exit(0);
}
} else {
/* parent process */
ret = waitpid(chi_pid, NULL, 0);
printf("parent process pid: %d, ppid: %d .\n", getpid(), getppid());
}
printf("main exit.\n");
return 0;
}
exec执行shell命令
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAXLINE 80
#define MAXCNT 20
void if_error(int stat_code, char *err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
char* cmd_parse(char *buf, char **args)
{
int cnt = 0;
char *cmd;
args[cnt++] = cmd = strtok(buf, " ");
do {
args[cnt] = strtok(NULL, " ");
} while (args[cnt++] != NULL);
return cmd;
}
int main(int argc, char **argv)
{
int chi_pid, ret;
char buf[MAXLINE], cmd_exit[MAXLINE];
char *cmd, *args[MAXCNT];
while (1) {
chi_pid = fork();
if_error(chi_pid, "fork");
if (chi_pid == 0) {
/* child process */
printf("input cmd: ");
fgets(buf, MAXLINE, stdin);
buf[strlen(buf)-1] = '\0';
/* parse cmd */
cmd = cmd_parse(buf, args);
ret = execvp(cmd, args);
if_error(ret, "execvp");
} else {
/* parent process */
chi_pid = wait(NULL);
if_error(chi_pid, "wait");
}
}
return 0;
}
进程间通信(IPC)
Linux下的IPC方式有共享内存, 消息队列, 管道, 信号等.
共享内存和消息队列需要root权限才能执行
- 共享内存
- shmget 创建/访问 共享内存对象
- shmat 把共享内存区对象映射到调用进程的地址空间
- shmdt 断开共享内存连接
- shmctl 对共享内存提供控制
- 消息队列
- msgget 创建/访问 消息队列
- msgrcv 从消息队列中获取消息
- msgsnd 把消息添加到消息队列中
- msgctl 对消息队列提供控制
- 管道
- pipe 为文件描述符数组建立无名管道
- mkfifo 创建命名管道(FIFO)文件
- 信号
- signal 设置信号捕捉句柄
- kill 发送信号
共享内存
利用内存进行通信的方式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define IPC_KEY 2048
#define MAXLINE 80
#define MAXSIZE MAXLINE*100
void if_error(int stat_code, char *err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
pid_t chi_pid;
int shm_id, ret;
char *shm_ptr;
struct shmid_ds shm_stat;
char buf[MAXLINE];
char output[MAXSIZE], content[MAXSIZE];
memset(content, 0, sizeof(content));
shm_id = shmget(IPC_KEY, MAXSIZE, IPC_CREAT);
if_error(shm_id, "shmget");
shm_ptr = shmat(shm_id, NULL, 0);
if_error(*(int*)shm_ptr, "shmat");
chi_pid = fork();
if_error(chi_pid, "fork");
if (chi_pid == 0) {
/* child process */
printf("input msg:\n");
while (1) {
/* input msg */
fgets(buf, MAXLINE, stdin);
buf[strlen(buf)-1] = '\0';
/* finish input msg */
if (strncmp(buf, "exit", 4) == 0) break;
strcat(content, buf);
}
/* cp content to shm_ptr */
memcpy(shm_ptr, content, MAXSIZE);
} else {
/* parent process */
chi_pid = wait(NULL);
if_error(chi_pid, "wait");
/* get content from shm_ptr */
memcpy(output, shm_ptr, MAXSIZE);
printf("%s\n", output);
/* get shm info */
shmctl(shm_id, IPC_STAT, &shm_stat);
printf("PID: %d, shared memory size is %ld\n", getpid(), shm_stat.shm_segsz);
/* disconnect */
ret = shmdt(shm_ptr);
if_error(ret, "shmdt");
}
return 0;
}
管道
半双工通信
无名管道
用于子进程和父进程的简单通信方式
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define MAXLINE 80
void if_error(int stat_code, char* err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
int fd[2], ret;
pid_t chi_pid;
int rd_n, wr_n;
char input[MAXLINE], buf[MAXLINE];
/* setup pipe
fd[0] read pipe
fd[1] write pipe
*/
ret = pipe(fd);
if_error(ret, "pipe");
chi_pid = fork();
if_error(chi_pid, "fork");
if (chi_pid == 0) {
/* child process */
close(fd[0]);
printf("child process send msg:\n");
while (1) {
/* send msg to parent process */
fgets(input, MAXLINE, stdin);
input[strlen(input)-1] = '\0';
wr_n = write(fd[1], input, strlen(input));
if_error(wr_n, "read");
/* child process exit */
if (strncmp(input, "exit", 4) == 0) {
printf("child process exit .\n");
exit(0);
}
}
} else {
/* parent process */
close(fd[1]);
while (1) {
/* recv msg from child process */
rd_n = read(fd[0], buf, MAXLINE);
if_error(rd_n, "read");
buf[rd_n] = '\0';
/* if recv exit msg */
if (strncmp(buf, "exit", 4) == 0) {
ret = wait(NULL);
if_error(ret, "wait");
printf("parent process exit .\n");
break;
}
printf("parent process recv msg: %s\n", buf);
}
}
return 0;
}
命名管道
用于两个进程间的简单通信
read.c
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAXLINE 80
void if_error(int stat_code, char* err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
int fd, rd_n, ret;
char *fifo_path = "./fifo";
char buf[MAXLINE];
/* setup fifo file */
ret = mkfifo(fifo_path, 0664);
if (ret < 0 && errno!=EEXIST) return -1;
/* open fifo file */
fd = open(fifo_path, O_RDONLY);
if_error(fd, "open");
printf("process %d opening FIFO with read-only .\n", getpid());
while (1) {
/* read pipe msg */
rd_n = read(fd, buf, MAXLINE);
if_error(rd_n, "read");
buf[rd_n] = '\0';
/* recv exit msg */
if (strncmp(buf, "exit", 4) == 0) break;
printf("recv pipe msg: %s\n", buf);
}
printf("read finished\n");
/* remove fifo file */
unlink(fifo_path);
close(fd);
return 0;
}
write.c
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAXLINE 80
void if_error(int stat_code, char* err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int main(int argc, char **argv)
{
int fd, ret, wr_n;
char *fifo_path = "./fifo";
char buf[MAXLINE];
/* setup fifo file */
ret = mkfifo(fifo_path, 0664);
if (ret < 0 && errno!=EEXIST) return -1;
/* open fifo file */
fd = open(fifo_path, O_WRONLY);
if_error(fd, "open");
printf("process %d opening FIFO with write-only .\n", getpid());
while (1) {
/* send pipe msg */
printf("send pipe msg:");
fgets(buf, MAXLINE, stdin);
buf[strlen(buf)-1] = '\0';
wr_n = write(fd, buf, strlen(buf));
if_error(wr_n, "write");
/* exit msg */
if (strncmp(buf, "exit", 4) == 0) break;
}
printf("write finished\n");
/* remove fifo file */
unlink(fifo_path);
close(fd);
return 0;
}
消息队列
与命名管道类似的一种通信机制, 可以发送数据块.
read.c
#include <unistd.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define IPC_KEY 1024
#define MAXLINE 80
void if_error(int stat_code, char* err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
struct msg_ds
{
char data[MAXLINE];
};
int main(int argc, char **argv)
{
int msq_id, rd_n;
struct msg_ds msg;
msq_id = msgget(IPC_KEY, IPC_CREAT);
if_error(msq_id, "msgget");
while (1) {
/* recv msg */
rd_n = msgrcv(msq_id, (void*)&msg, MAXLINE, 0, 0);
if_error(rd_n, "msgrcv");
msg.data[rd_n] = '\0';
/* exit msg */
if (strncmp(msg.data, "exit", 4) == 0) break;
printf("recv msg: %s\n", msg.data);
}
msgctl(msq_id, IPC_RMID, 0);
return 0;
}
write.c
#include <unistd.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define IPC_KEY 1024
#define MAXLINE 80
void if_error(int stat_code, char* err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
struct msg_ds
{
char data[MAXLINE];
};
int main(int argc, char **argv)
{
int msq_id, wr_n;
struct msg_ds msg;
msq_id = msgget(IPC_KEY, IPC_CREAT);
if_error(msq_id, "msgget");
while (1) {
/* send msg */
printf("send msg: ");
fgets(msg.data, MAXLINE, stdin);
msg.data[strlen(msg.data)-1] = '\0';
wr_n = msgsnd(msq_id, &msg, MAXLINE, 0);
if_error(wr_n, "msgsnd");
/* exit msg */
if (strncmp(msg.data, "exit", 4) == 0) break;
}
msgctl(msq_id, IPC_RMID, 0);
return 0;
}
信号
发送信号和捕捉信号的通信方式
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
void if_error(int stat_code, char *err_msg)
{
if (stat_code < 0) {
perror(err_msg);
exit(errno);
}
}
int get_sig = 0;
void sig_alarm_handler(int sig)
{
get_sig = 1;
}
int main(int argc, char **argv)
{
pid_t chi_pid;
chi_pid = fork();
if_error(chi_pid, "fork");
/* setup signal handler */
signal(SIGALRM, sig_alarm_handler);
if (chi_pid == 0) {
/* child process */
sleep(5);
kill(getppid(), SIGALRM);
exit(0);
} else {
/* parent process */
while (1) {
sleep(1);
/* check if recv sig */
if (get_sig) {
printf("get signal SIGALRM(%d)\n", SIGALRM);
break;
} else {
printf("not get signal\n");
}
}
}
return 0;
}