linux-系统调用和进程通信

本人的Linux作业

0.前情提要

Linux内核编译:

Linux kernel编译记录 | lnm011223の私密房间

查看内核版本:

1
uname -r

1.系统调用

创建两个文件:

test.c

Makefile

并放在同一目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//test.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

MODULE_LICENSE("Dual BSD/GPL");

static int init(void){
printk(KERN_INFO "20192132039\nmodi\n");
return 0;
}

static void clearup(void){
printk(KERN_INFO "bye\n");
}

module_init(init);
module_exit(clearup);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ifneq ($(KERNELRELEASE),)
r
mymodule-objs := test.o
obj-m := test.o

else
PWD := $(shell pwd)
KVER ?= $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build

all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions

endif

然后打开终端输入以下命令来安装模块并查看信息

1
2
3
4
make
insmod test.ko
dmesg -c
dmesg

卸载模块并查看信息

1
2
rmmod test.ko
dmesg

2.IPC

2.1.管道

无名管道应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdlib.h>
#include<string.h>
main(){
int p1,fd[2];
char outpipe[50];
char inpipe[50];
pipe(fd);
while((p1=fork())==-1);
if(p1==0){
strcpy(inpipe,"This is a message!");
write(fd[1],inpipe,50);
exit(0);
}
else{
wait(0);
read(fd[0],outpipe,50);
printf("%s\n",outpipe);
exit(0);
}
}

改造:

父进程创建两个子进程P1、P2,P1向管道写数据,P2从管道读数据并显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<stdlib.h>
#include<string.h>
main(){
int p1,p2,fd[2];
char outpipe[50];
char inpipe[50];
pipe(fd);
while((p1=fork())==-1);
while((p2=fork())==-1);
if(p1==0){
strcpy(inpipe,"Pipe Message Test");
write(fd[1],inpipe,50);
exit(0);
}
if(p2==0){
read(fd[0],outpipe,50);
printf("%s\n",outpipe);
exit(0);
}
if(p2!=0 and p1!=0){
wait(0);
exit(0);
}
}

2.2.信号

2.2.1.软中断实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//4-1.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int k;

void int_func(int sig){
k=0;
printf("int_func\n");
}

void main(){
signal(SIGINT,int_func);
k=1;
while(k == 1){
printf("Hello!\n");
}
printf("OK!\n");
exit(0);
}

改造:要求按3次Ctrl+C后才结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//4-1.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int k;

void int_func(int sig){
k=k-1;
printf("int_func\n");
}

void main(){
signal(SIGINT,int_func);
k=3;
while(k>0){
printf("Hello!\n");
}
printf("OK!\n");
exit(0);
}

改造:将signal()语句和函数删除后重新编译看结果有何变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//4-1.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int k;

void int_func(int sig){
k=0;
printf("int_func\n");
}

void main(){
//signal(SIGINT,int_func);
k=1;
while(k == 1){
printf("Hello!\n");
}
printf("OK!\n");
exit(0);
}

2.2.2.使用软中断实现父子进程同步

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
//child.c
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

int k1;
void int_fun1()
{
k1=0;
}
main()
{
int k,p1;
while((p1=fork())==-1);
if(p1>0)
{
for(k=1;k<4;k++)
{
printf("How are you!\n");
sleep(1);//延时函数sleep() ,延时1秒
}
kill(p1,12);
wait(0);
printf("OK!\n");
exit(0);
}
else
{
signal(12,int_fun1);
k1=1;
while(k1==1)
{
printf("I am child\n");
sleep(1);
}
printf("Child exited!\n");
exit(0);
}
}

改造:一定要子进程先执行

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
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

int k1;
void int_fun1()
{
k1=0;
}
main()
{
int k,p1;
while((p1=fork())==-1);
if(p1>0)
{
for(k=1;k<4;k++)
{
sleep(1);//延时函数sleep() ,延时1秒
printf("How are you!\n");

}
kill(p1,12);
wait(0);
printf("OK!\n");
exit(0);
}
else
{
signal(12,int_fun1);
k1=1;
while(k1==1)
{
printf("I am child\n");
sleep(1);
}
printf("Child exited!\n");
exit(0);
}
}

把sleep(1)提到 printf 前面即可


2.3.消息传递

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
#Sndfile.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<linux/msg.h>
#define MAXMSG 512
struct my_msg{

long int my_msg_type;
int i;
char some_text[MAXMSG];

}msg;

main(){
int msgid;
char buffer[BUFSIZ];
msgid=msgget(12,0666|IPC_CREAT);

while(1){
puts("Enter some text:");
fgets(buffer,BUFSIZ,stdin);
msg.i++;
printf("i=%d\n",msg.i);
msg.my_msg_type=3;
strcpy(msg.some_text,buffer);
msgsnd(msgid,&msg,MAXMSG,0);
if(strncmp(msg.some_text,"end",3)==0)
break;
}
exit(0);
}
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
Rcvfile.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<linux/msg.h>
#define MAXMSG 512
struct my_msg{

long int my_msg_type;
int i;
char some_text[MAXMSG];

}msg;

main(){
int msgid;
msg.my_msg_type=3;

msgid=msgget(12,0666|IPC_CREAT);
while(1){
msgrcv(msgid,&msg,BUFSIZ,msg.my_msg_type,0);
printf("You wrote:%s and i=%d\n",msg.some_text,msg.i);
if(strncmp(msg.some_text,"end",3)==0)
break;

}
msgctl(msgid,IPC_RMID,0);
exit(0);
}

2.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
//Sndshm.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
//#include<linux/shm.h> 把这里的头文件改成了下面的
#include<sys/shm.h>
main(){
int shmid;
char *viraddr;
char buffer[BUFSIZ];

shmid=shmget(1234,BUFSIZ,0666|IPC_CREAT); //创建共享内存
viraddr=(char*)shmat(shmid,0,0); //附接到共享内存

while(1){
puts("Enter some text:"); //提示用户输入信息
fgets(buffer,BUFSIZ,stdin); //将标准输入送入缓冲区
strcat(viraddr,buffer); //采用追加方式写到共享内存
if(strncmp(buffer,"end",3)==0) //输入的字符串为“end”时,终止循环
break;

}
shmdt(viraddr); //切断与共享内存的链接
exit(0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Rcvshm.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
//#include<linux/shm.h>
#include<sys/shm.h>
main(){
int shmid;
char *viraddr;


shmid=shmget(1234,BUFSIZ,0666|IPC_CREAT); //创建共享内存
viraddr=(char*)shmat(shmid,0,0); //附接到共享内存

printf("Your message is :%s",viraddr); //输出共享内存的内容

shmdt(viraddr); //切断与共享内存的链接
shmctl(shmid,IPC_RMID,0); //释放共享内存
exit(0);
}

3.代码实践2

作业最复杂的一部分,先把代码放了

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//A.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/shm.h>

#define BUFSIZE 4096

char ask = 'D';
char ans = 'R';

#include<sys/msg.h>
#define MAXMSG 512
struct my_msg{

long int my_msg_type;
char signalB;
char signalA;
int headM1;
int sizeM1;

}msg;



int main(){
int shmid,msgid;
char *viraddrA; //link to M1
char *viraddrB; //link to M2
char bufferM1[BUFSIZE];
char bufferM2[BUFSIZE];
int cycle = 10;
int i,count;

msg.sizeM1 = 0;
msg.headM1 = 0;
msg.signalA = '\0';
msg.signalB = '\0';
msg.my_msg_type=5;
printf("Communication begins\n");


while(cycle--){

shmid=shmget(5067,BUFSIZE,0666|IPC_CREAT); //创建共享内存
viraddrA=(char*)shmat(shmid,0,0); //附接到共享内存


unsigned int ra = rand();
ra = 1 + ra % 10;
if(ra + msg.sizeM1 > 4096){
ra = 4096 - msg.sizeM1;
}

for(i=0;i<ra;i++){
count = (msg.headM1 + msg.sizeM1 + i)%4096;
bufferM1[count] = ask;
}
msg.sizeM1 += ra;
strcat(viraddrA,bufferM1); //A send data to M1

msgid=msgget(1,0666|IPC_CREAT);
msg.signalA = '?';
msgsnd(msgid,&msg,sizeof(msg),0);
printf("M1 is ready.\n");

sleep(10);


msgrcv(msgid,&msg,sizeof(msg),msg.my_msg_type,0);

if(msg.signalB == '!'){
shmid=shmget(59474,BUFSIZE,0666|IPC_CREAT); //创建共享内存
viraddrB=(char*)shmat(shmid,0,0);
msg.signalB = '\0';
for(i = 0;i<4096;i++){
bufferM2[i] = '\0';
}
printf("M2 was consumed\n");
msgsnd(msgid,&msg,sizeof(msg),0);
strcat(viraddrB,bufferM2);

shmdt(viraddrB);
}
else{
printf("Waiting B.\n");
sleep(5);
}

shmdt(viraddrA); //切断与共享内存的链接
}
printf("Communication over.");
exit(0);
}
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//B.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/msg.h>
#define BUFSIZE 4096
#define MAXMSG 512

struct my_msg{

long int my_msg_type; // 值为5,因为结构体有五个成员
char signalB; //'!'表示A要B消费M1
char signalA; //'?'表示B要A消费M2
int headM1;
int sizeM1;

}msg;

char ask = 'D';
char ans = 'R';

int main(){


int shmid,msgid;
char *viraddrA; //link to M1
char *viraddrB; //link to M2
char bufferM1[BUFSIZE];
char bufferM2[BUFSIZE];

int cycle = 12;
int headM2 = 0;
int sizeM2 = 0;

int i,count;

msg.sizeM1 = 0;
msg.headM1 = 0;
msg.signalA = '\0';
msg.signalB = '\0';
msg.my_msg_type=5;

printf("Communication begins\n");
while(cycle--){


shmid=shmget(59474,BUFSIZE,0666|IPC_CREAT); //创建共享内存
viraddrB=(char*)shmat(shmid,0,0);


unsigned int rb = rand();
rb = 1+ rb % 10;

msgid=msgget(1,0666|IPC_CREAT);
msgrcv(msgid,&msg,sizeof(msg),msg.my_msg_type,0);

if(msg.signalA == '?'){
msg.signalB = '!';
shmid=shmget(5067,BUFSIZE,0666|IPC_CREAT); //创建共享内存
viraddrA=(char*)shmat(shmid,0,0); //附接到共享内存

if(rb > msg.sizeM1){
rb = msg.sizeM1;
}
if( rb + sizeM2 > 4096){
rb = 4096 - sizeM2;
}
for(i = 0;i<4096;i++){
bufferM1[i] = *(viraddrA+i);
}
for(i = 0;i<msg.sizeM1;i++){
count = sizeM2 + headM2 + i;
bufferM2[count] = ans;
count = msg.sizeM1 + msg.headM1 - i;
bufferM1[count] = '\0';
}
printf("M2 is ready.\n");
sizeM2 += rb;
msg.sizeM1 -= rb;
if(msg.sizeM1 == 0){
printf("M1 is empty.\n");
msg.signalA = '\0';
}


msgsnd(msgid,&msg,sizeof(msg),0);

strcat(viraddrB,bufferM2);
strcat(viraddrA,bufferM1);

shmdt(viraddrA);
}
else{
printf("Waiting A.\n");
}

sleep(10);
//切断与共享内存的链接
shmdt(viraddrB);
}
printf("Communication over.");
exit(0);
}

​ 基本的编码思想是开辟两个数组分别存储M1和M2,然后利用共享内存通信实现M1和M2的跨进程调用。调用的时机点切入则由消息缓冲通信实现。开发中发现把数组实现成环形作用不大(但我还是勉强实现了,方法是把起始下标和容量存起来),因为填入M1或M2的所有数据都是一样的,生产的时候下标递增而消费的时候下标递减即可(可能有必要实现先消费旧信息这个功能,只需要把M2的起始下标和容量也存起来即可,但我的代码没有呈现)。有一个问题是,A收到B的应答信息后的具体行动。我的做法是将M2中所有的应答信息消费完,而因为如此msg不用传sizeM2,因为A会一次性把整个M2消费完,直接遍历就完事了;此外就是A没收到B的应答信息时应不应该继续生产data。为了图省事我没有额外做一个判断,所以会继续生产直到buffer满。

​ 需要注意的是mod运算对于实现循环非常必要,无论怎样实现都要注意这一点。

上面的代码写完之后我就没有review了,所以可能有很多不足之处(懒)

文章作者: 莫折眉
文章链接: https://m0d1.top/2021/11/05/aboutlinux/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 M0D1.TOP