您的位置:首页 > 运维架构 > Linux

Linux C 进程与进程间通信

2016-12-14 14:14 495 查看

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
4000
_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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: