Linux系统编程入门 - 进程管理与IPC通信

Linux系统编程入门 - 进程管理与IPC通信

前言

Linux系统编程是深入理解操作系统工作原理的重要途径。本文将介绍Linux下的进程管理、进程间通信(IPC)机制,以及相关的系统调用。

进程基础

进程的创建

在Linux中,进程可以通过fork()系统调用创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
pid_t pid;

printf("Before fork\n");

pid = fork();

if (pid == 0) {
// 子进程
printf("Child process: PID = %d, Parent PID = %d\n",
getpid(), getppid());
sleep(2);
printf("Child process exiting\n");
exit(0);
} else if (pid > 0) {
// 父进程
printf("Parent process: PID = %d, Child PID = %d\n",
getpid(), pid);

int status;
wait(&status); // 等待子进程结束
printf("Child process finished with status: %d\n", status);
} else {
// fork失败
perror("fork failed");
exit(1);
}

return 0;
}

进程替换

使用exec系列函数可以替换当前进程的映像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
pid_t pid = fork();

if (pid == 0) {
// 子进程执行新程序
char *args[] = {"ls", "-la", NULL};
char *env[] = {NULL};

execve("/bin/ls", args, env);
perror("execve failed");
exit(1);
} else if (pid > 0) {
// 父进程等待子进程
int status;
wait(&status);
printf("Child process completed\n");
} else {
perror("fork failed");
exit(1);
}

return 0;
}

进程间通信(IPC)

1. 管道(Pipe)

管道是最简单的IPC机制,用于父子进程间的通信:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main() {
int pipefd[2];
char buffer[256];
const char *message = "Hello from parent!";

// 创建管道
if (pipe(pipefd) == -1) {
perror("pipe failed");
exit(1);
}

pid_t pid = fork();

if (pid == 0) {
// 子进程:读取数据
close(pipefd[1]); // 关闭写端

read(pipefd[0], buffer, sizeof(buffer));
printf("Child received: %s\n", buffer);

close(pipefd[0]);
exit(0);
} else if (pid > 0) {
// 父进程:写入数据
close(pipefd[0]); // 关闭读端

write(pipefd[1], message, strlen(message) + 1);
close(pipefd[1]);

wait(NULL);
printf("Parent finished\n");
} else {
perror("fork failed");
exit(1);
}

return 0;
}

2. 命名管道(FIFO)

命名管道允许不相关进程间通信:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// writer.c
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main() {
const char *fifo_name = "/tmp/my_fifo";
char message[] = "Hello from writer!";

// 创建命名管道
mkfifo(fifo_name, 0666);

int fd = open(fifo_name, O_WRONLY);
if (fd == -1) {
perror("open failed");
return 1;
}

write(fd, message, strlen(message) + 1);
close(fd);

printf("Writer finished\n");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// reader.c
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

int main() {
const char *fifo_name = "/tmp/my_fifo";
char buffer[256];

int fd = open(fifo_name, O_RDONLY);
if (fd == -1) {
perror("open failed");
return 1;
}

read(fd, buffer, sizeof(buffer));
printf("Reader received: %s\n", buffer);

close(fd);
unlink(fifo_name); // 删除命名管道

return 0;
}

3. 共享内存

共享内存是最快的IPC方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <sys/wait.h>

int main() {
key_t key = ftok("/tmp", 'A');
int shmid;
char *shared_memory;

// 创建共享内存段
shmid = shmget(key, 1024, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget failed");
exit(1);
}

// 附加到共享内存
shared_memory = shmat(shmid, NULL, 0);
if (shared_memory == (char*)-1) {
perror("shmat failed");
exit(1);
}

pid_t pid = fork();

if (pid == 0) {
// 子进程:读取数据
sleep(1); // 等待父进程写入
printf("Child read: %s\n", shared_memory);

// 分离共享内存
shmdt(shared_memory);
exit(0);
} else if (pid > 0) {
// 父进程:写入数据
strcpy(shared_memory, "Hello from shared memory!");
printf("Parent wrote data\n");

wait(NULL);

// 分离共享内存
shmdt(shared_memory);

// 删除共享内存段
shmctl(shmid, IPC_RMID, NULL);
printf("Parent finished\n");
} else {
perror("fork failed");
exit(1);
}

return 0;
}

4. 信号量

信号量用于进程同步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>

int main() {
key_t key = ftok("/tmp", 'B');
int semid;

// 创建信号量集
semid = semget(key, 1, IPC_CREAT | 0666);
if (semid == -1) {
perror("semget failed");
exit(1);
}

// 初始化信号量
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} sem_union;

sem_union.val = 1;
semctl(semid, 0, SETVAL, sem_union);

pid_t pid = fork();

if (pid == 0) {
// 子进程:等待信号量
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = -1; // P操作
sem_op.sem_flg = 0;

printf("Child waiting for semaphore...\n");
semop(semid, &sem_op, 1);
printf("Child acquired semaphore\n");

sleep(2);

// 释放信号量
sem_op.sem_op = 1; // V操作
semop(semid, &sem_op, 1);
printf("Child released semaphore\n");

exit(0);
} else if (pid > 0) {
// 父进程:等待信号量
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = -1; // P操作
sem_op.sem_flg = 0;

printf("Parent waiting for semaphore...\n");
semop(semid, &sem_op, 1);
printf("Parent acquired semaphore\n");

sleep(2);

// 释放信号量
sem_op.sem_op = 1; // V操作
semop(semid, &sem_op, 1);
printf("Parent released semaphore\n");

wait(NULL);

// 删除信号量集
semctl(semid, 0, IPC_RMID);
printf("Parent finished\n");
} else {
perror("fork failed");
exit(1);
}

return 0;
}

总结

Linux系统编程涉及多个重要概念:

  1. 进程管理:fork、exec、wait等系统调用
  2. 进程间通信:管道、命名管道、共享内存、信号量
  3. 同步机制:信号量、互斥锁等

掌握这些基础概念对于深入理解操作系统和开发系统级软件非常重要。在实际应用中,需要根据具体需求选择合适的IPC机制。