请教一个Linux下C语言的进程间的信号问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了请教一个Linux下C语言的进程间的信号问题相关的知识,希望对你有一定的参考价值。

linux中的进程通信分为三个部分:低级通信,管道通信和进程间通信IPC(inter process communication)。linux的低级通信主要用来传递进程的控制信号——文件锁和软中断信号机制。linux的进程间通信IPC有三个部分——①信号量,②共享内存和③消息队列。以下是我编写的linux进程通信的C语言实现代码。操作系统为redhat9.0,编辑器为vi,编译器采用gcc。下面所有实现代码均已经通过测试,运行无误。

一.低级通信--信号通信

signal.c

#include
#include
#include

/*捕捉到信号sig之后,执行预先预定的动作函数*/
void sig_alarm(int sig)

printf("---the signal received is %d. /n", sig);
signal(SIGINT, SIG_DFL); //SIGINT终端中断信号,SIG_DFL:恢复默认行为,SIN_IGN:忽略信号


int main()

signal(SIGINT, sig_alarm);//捕捉终端中断信号
while(1)

printf("waiting here!/n");
sleep(1);

return 0;


二.管道通信

pipe.c

#include
#define BUFFER_SIZE 30

int main()

int x;
int fd[2];
char buf[BUFFER_SIZE];
char s[BUFFER_SIZE];
pipe(fd);//创建管道
while((x=fork())==-1);//创建管道失败时,进入循环

/*进入子进程,子进程向管道中写入一个字符串*/
if(x==0)

sprintf(buf,"This is an example of pipe!/n");
write(fd[1],buf,BUFFER_SIZE);
exit(0);


/*进入父进程,父进程从管道的另一端读出刚才写入的字符串*/
else

wait(0);//等待子进程结束
read(fd[0],s,BUFFER_SIZE);//读出字符串,并将其储存在char s[]中
printf("%s",s);//打印字符串

return 0;


三.进程间通信——IPC

①信号量通信

sem.c

#include
#include
#include
#include types.h>
#include ipc.h>
#include sem.h>

/*联合体变量*/
union semun

int val; //信号量初始值
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
;

/*函数声明,信号量定义*/
static int set_semvalue(void); //设置信号量
static void del_semvalue(void);//删除信号量
static int semaphore_p(void); //执行P操作
static int semaphore_v(void); //执行V操作
static int sem_id; //信号量标识符

int main(int argc, char *argv[])

int i;
int pause_time;
char op_char = \'O\';
srand((unsigned int)getpid());
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);//创建一个信号量,IPC_CREAT表示创建一个新的信号量

/*如果有参数,设置信号量,修改字符*/
if (argc > 1)

if (!set_semvalue())

fprintf(stderr, "Failed to initialize semaphore/n");
exit(EXIT_FAILURE);

op_char = \'X\';
sleep(5);

for(i = 0; i < 10; i++)


/*执行P操作*/
if (!semaphore_p())
exit(EXIT_FAILURE);
printf("%c", op_char);
fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%c", op_char);
fflush(stdout);

/*执行V操作*/
if (!semaphore_v())
exit(EXIT_FAILURE);
pause_time = rand() % 2;
sleep(pause_time);

printf("/n%d - finished/n", getpid());
if (argc > 1)

sleep(10);
del_semvalue(); //删除信号量

exit(EXIT_SUCCESS);


/*设置信号量*/
static int set_semvalue(void)

union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
return(0);

return(1);


/*删除信号量*/
static void del_semvalue(void)

union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore/n");


/*执行P操作*/
static int semaphore_p(void)

struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1; /* P() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1)

fprintf(stderr, "semaphore_p failed/n");
return(0);

return(1);


/*执行V操作*/
static int semaphore_v(void)

struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1; /* V() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1)

fprintf(stderr, "semaphore_v failed/n");
return(0);

return(1);


②消息队列通信

send.c

#include
#include
#include
#include
#include
#include types.h>
#include ipc.h>
#include msg.h>
#define MAX_TEXT 512

/*用于消息收发的结构体--my_msg_type:消息类型,some_text:消息正文*/
struct my_msg_st

long int my_msg_type;
char some_text[MAX_TEXT];
;

int main()

int running = 1;//程序运行标识符
struct my_msg_st some_data;
int msgid;//消息队列标识符
char buffer[BUFSIZ];

/*创建与接受者相同的消息队列*/
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1)

fprintf(stderr, "msgget failed with error: %d/n", errno);
exit(EXIT_FAILURE);


/*向消息队列中发送消息*/
while(running)

printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
some_data.my_msg_type = 1;
strcpy(some_data.some_text, buffer);
if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1)

fprintf(stderr, "msgsnd failed/n");
exit(EXIT_FAILURE);

if (strncmp(buffer, "end", 3) == 0)

running = 0;


exit(EXIT_SUCCESS);


receive.c

#include
#include
#include
#include
#include
#include types.h>
#include ipc.h>
#include msg.h>

/*用于消息收发的结构体--my_msg_type:消息类型,some_text:消息正文*/
struct my_msg_st

long int my_msg_type;
char some_text[BUFSIZ];
;

int main()

int running = 1;//程序运行标识符
int msgid; //消息队列标识符
struct my_msg_st some_data;
long int msg_to_receive = 0;//接收消息的类型--0表示msgid队列上的第一个消息

/*创建消息队列*/
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1)

fprintf(stderr, "msgget failed with error: %d/n", errno);
exit(EXIT_FAILURE);


/*接收消息*/
while(running)

if (msgrcv(msgid, (void *)&some_data, BUFSIZ,msg_to_receive, 0) == -1)

fprintf(stderr, "msgrcv failed with error: %d/n", errno);
exit(EXIT_FAILURE);

printf("You wrote: %s", some_data.some_text);
if (strncmp(some_data.some_text, "end", 3) == 0)

running = 0;



/*删除消息队列*/
if (msgctl(msgid, IPC_RMID, 0) == -1)

fprintf(stderr, "msgctl(IPC_RMID) failed/n");
exit(EXIT_FAILURE);

exit(EXIT_SUCCESS);


③共享内存通信

share.h

#define TEXT_SZ 2048 //申请共享内存大小
struct shared_use_st

int written_by_you; //written_by_you为1时表示有数据写入,为0时表示数据已经被消费者提走
char some_text[TEXT_SZ];
;

producer.c

#include
#include
#include
#include
#include types.h>
#include ipc.h>
#include shm.h>
#include "share.h"

int main()

int running = 1; //程序运行标志位
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid; //共享内存标识符

/*创建共享内存*/
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
if (shmid == -1)

fprintf(stderr, "shmget failed/n");
exit(EXIT_FAILURE);


/*将共享内存连接到一个进程的地址空间中*/
shared_memory = shmat(shmid, (void *)0, 0);//指向共享内存第一个字节的指针
if (shared_memory == (void *)-1)

fprintf(stderr, "shmat failed/n");
exit(EXIT_FAILURE);

printf("Memory attached at %X/n", (int)shared_memory);
shared_stuff = (struct shared_use_st *)shared_memory;

/*生产者写入数据*/
while(running)

while(shared_stuff->written_by_you == 1)

sleep(1);
printf("waiting for client.../n");

printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
shared_stuff->written_by_you = 1;
if (strncmp(buffer, "end", 3) == 0)

running = 0;



/*该函数用来将共享内存从当前进程中分离,仅使得当前进程不再能使用该共享内存*/
if (shmdt(shared_memory) == -1)

fprintf(stderr, "shmdt failed/n");
exit(EXIT_FAILURE);

printf("producer exit./n");
exit(EXIT_SUCCESS);


customer.c

#include
#include
#include
#include
#include types.h>
#include ipc.h>
#include shm.h>
#include "share.h"

int main()

int running = 1;//程序运行标志位
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
int shmid; //共享内存标识符
srand((unsigned int)getpid());

/*创建共享内存*/
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
if (shmid == -1)

fprintf(stderr, "shmget failed/n");
exit(EXIT_FAILURE);


/*将共享内存连接到一个进程的地址空间中*/
shared_memory = shmat(shmid, (void *)0, 0);//指向共享内存第一个字节的指针
if (shared_memory == (void *)-1)

fprintf(stderr, "shmat failed/n");
exit(EXIT_FAILURE);

printf("Memory attached at %X/n", (int)shared_memory);
shared_stuff = (struct shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;

/*消费者读取数据*/
while(running)

if (shared_stuff->written_by_you)

printf("You wrote: %s", shared_stuff->some_text);
sleep( rand() % 4 );
shared_stuff->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0)

running = 0;




/*该函数用来将共享内存从当前进程中分离,仅使得当前进程不再能使用该共享内存*/
if (shmdt(shared_memory) == -1)

fprintf(stderr, "shmdt failed/n");
exit(EXIT_FAILURE);


/*将共享内存删除,所有进程均不能再访问该共享内存*/
if (shmctl(shmid, IPC_RMID, 0) == -1)

fprintf(stderr, "shmctl(IPC_RMID) failed/n");
exit(EXIT_FAILURE);

exit(EXIT_SUCCESS);


摘自:
参考技术A 说问题

Linux C与C++一线开发实践之四 Linux进程间的通信

Linux中的进程为了能在同一项任务上协调工作,它们彼此之间必须能够进行通信。下面主要介绍Linux常用的3种通信方式:信号、管道和消息队列。 效果差别不大,熟练一种基本可以应对一般的一线开发场景了。(这里讲的进程间通信是指同一台物理机上多个进程间通信,跨机器一般是socket通信)

信号

信号可以说是最早引入UNIX系统中的进程间通信方式之一,Linux同样支持这种通信方式。信号是很短的信息,可以被发送到一个进程或者一组进程,一般就是标志信号的一个数表示。它可以从键盘中断产生,进程在虚拟内存的非法访问等错误环境下也会产生,也可以被shell程序用来向其子进程发送任务控制命令等。可以简单理解为系统预设的一系列特定事件触发的监听处理方式。
这些信号可以用kill -l查看:

上面列表中,编号1~31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号32~64的信号是后面扩展的,称可靠信号(实时信号)。不可靠信号与可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。每个信号含义自行wiki。
进程接收到某信号可以采取下面3中行为之一:

  1. 忽略信号(SIGKILL和SIGSTOP这两个信号不能被忽略)
  2. 执行与这个信号相关的默认操作。
  3. 调用自定义的信号处理函数
    由此可见信号作为进程间通信只能处理一些预定义的系统特定事件,且无法传递数据。实际项目一般用于处理程序异常或特定shell命令操作。

管道

管道,用于连接读进程和写进程,以实现它们之间通信的共享文件。 它能传送大量数据,数据先进先出,而且一般是单向的。写进程将数据写入管道,读进程从管道读取数据,腾出空间以便写进程写入新的数据,所有数据只能读取一次。Linux管道是一个固定大小的缓冲区,一般为一个页面,即4kb。
所以管道是读写成对的,必须要同步,如果一个进程试图写入一个已满的管道,在默认情况,系统会自动堵塞该进程,直到管道能够有空间接收数据。同样,如果试图读一个空管道,进程也会阻塞,直到管道有可读的数据。此外,如果一个进程以读方式打开一个管道,而没有另外的进程以写方式打开该管道,则同样会造成该进程阻塞。有写没有读同理。所以管道使用读写必须是成对的。 而且只能在有公共祖先的进程间使用管道。
在Shell下,可以通过符合 “|” 来使用管道。例如:ls -l | grep cxx,ls是列出当前文件夹下所有目录和文件,现在把本来要输出到屏幕上的数据通过管道输出到grep进程中,作为grep的输入,然后grep进程对输入信息进行筛选,把存在cxx字符串的那行打印在屏幕上。
下面我们来看一个父子进程使用管道的通信示例:

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>

void sys_err(const char *str)

	perror(str);
	exit(1);


int main(void)

	pid_t pid;
	char buf[1024];
	int fd[2];
	char *p = "test for pipe\\n";
	// 创建管道, 同时创建了读写描述符
	if(pipe(fd) == -1) 
		sys_err("pipe");
	
	// 创建子进程
	pid = fork();
	if(pid < 0) 
		sys_err("fork err");
	 else if(pid == 0) 
		close(fd[1]); //子进程关闭写描述符
		printf("child process wait to read:\\n");
		int len = read(fd[0], buf, sizeof(buf));
		write(STDOUT_FILENO, buf, len);
		close(fd[0]);
	 else 
		close(fd[0]); // 父进程关闭读描述符
		write(fd[1], p, strlen(p));
		wait(NULL);  //等待其子进程中断或结束,不然一直阻塞自己
		close(fd[1]);
	
	return 0;

消息队列

从许多方面来看,消息队列类似于有名管道,但是没有像管道那样那么多限制与复杂的关联。消息队列提供了一种在两个不相关的进程之间传递数据的简单高效的方法。独立于发送与接收进程,消息队列也是一块数据块,每一个数据块可以看作有一个类型,而接收进程可以独立接收具有不同类型的数据块。完全不用考虑像管道那种必须读写的同步问题。这应该是一种比管道更好的解决方案。
消息队列的发送和接收示例:

// 接收程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st

    long int my_msg_type;
    char some_text[BUFSIZ];
;
int main()

    int running = 1;
    int msgid;
    struct my_msg_st some_data;
    long int msg_to_receive = 0;

	//设置消息队列:
    msgid = msgget((key_t)1234,0666|IPC_CREAT);
    if(msgid == -1)
    
        fprintf(stderr,"msgget failed with error: %d\\n", errno);
        exit(EXIT_FAILURE);
    
    
  	//接收消息队列中的消息直到遇到一个end消息。最后,消息队列被删除:
    while(running)
    
        if(msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
        
            fprintf(stderr, "msgrcv failed with errno: %d\\n", errno);
            exit(EXIT_FAILURE);
        

        printf("You wrote: %s", some_data.some_text);
        if(strncmp(some_data.some_text, "end", 3)==0)
        
            running = 0;
        
    
 
    if(msgctl(msgid, IPC_RMID, 0)==-1)
    
        fprintf(stderr, "msgctl(IPC_RMID) failed\\n");
        exit(EXIT_FAILURE);
    
    exit(EXIT_SUCCESS);


// 发送程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st

    long int my_msg_type;
    char some_text[MAX_TEXT];
;

int main()

    int running = 1;
    struct my_msg_st some_data;
    int msgid;
    char buffer[BUFSIZ];
    msgid = msgget((key_t)1234, 0666|IPC_CREAT);
    if(msgid==-1)
    
        fprintf(stderr,"msgget failed with errno: %d\\n", errno);
        exit(EXIT_FAILURE);
    
     while(running)
    
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        some_data.my_msg_type = 1;
        strcpy(some_data.some_text, buffer);
        if(msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0)==-1)
        
            fprintf(stderr, "msgsnd failed\\n");
            exit(EXIT_FAILURE);
        
        if(strncmp(buffer, "end", 3) == 0)
        
            running = 0;
        
    
     exit(EXIT_SUCCESS);



以上是关于请教一个Linux下C语言的进程间的信号问题的主要内容,如果未能解决你的问题,请参考以下文章

linux 下进程间的同步机制都有哪些

Linux C语言无名信号量与有名信号量(无名使用 <semaphore.h>,有名信号量<sys/sem.h>)

Linux C语言无名信号量与有名信号量(无名使用 <semaphore.h>,有名信号量<sys/sem.h>)

Linux C与C++一线开发实践之四 Linux进程间的通信

Linux C与C++一线开发实践之四 Linux进程间的通信

linux下 进程信号量和线程信号量的区别和联系是啥