进程间通信及同步

1、pipe(无名管道)(最简单,仅支持有血缘关系的)
半双工通信
#include <unistd.h>
int pipe(int pipefd[2]);
// pipefd 读写描述符, fd[0]代表读 fd[1]代表写
// 返回值:成功返回-1 成功返回0
// 创建管道在fork之前
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(char **argv, int argc)
{
	int fd[2];
	pipe(fd);
	
	pid_t pid = fork();
	if(pid == 0)
	{
		write(fd[1], "Hello!", strlen("Hello!"));
		char buf[32] = {0};
		sleep(1);
		int ret = read(fd[0], buf, sizeof(buf));
		if(ret > 0)
			printf("pid = %d ret: %s\n", pid, buf);
	}
	else if(pid > 0)
	{
		char buf[8] = {0};
		int ret = read(fd[0], buf, sizeof(buf));
		
		if(ret > 0)
		{
			printf("pid = %d read: %s\n", pid, buf);
			int len = write(fd[1], "read Successfully!", strlen("read Successfully!"));
		}
		pid_t wpid ;
		while((wpid = waitpid(-1, NULL, WNOHANG)) == 0)
		{
			sleep(1);
		}
		if(wpid > 0)
		{
			printf("Child exit with pid = %d!\n", wpid);
		}      
	}
	close(fd[0]);
	close(fd[1]);
	return 0;
}
父进程认为还有写端存在,就有可能还有人发送数据,继续等待
所以就需要设置管道方向,子进程只写 关闭读端close(fd[0]),
						父进程只读 关闭写端close(fd[1])
管道读写行为:
	写端全部关闭:read读到0,相当于读到文件末尾
	写端没有全部关闭
		没有数据,read阻塞,fcntl函数可以更改非阻塞
	读端全部关闭:
		报错误,产生一个信号,SIGPIPE,程序异常终止
	读端没全部关闭:
		管道已满:write阻塞
		管道未满:write正常写入
查看管道大小
命令查看 ulimit -a
long fpathconf(int fd, int name);  name 为宏定义  _PC_PIPE_BUF
2、fifo(有名管道,支持非亲缘进程,双工)
1、创建管道伪文件 mkfifo myfifo 也可以用函数创建
	#include <sys/types.h>
	#include <sys/stat.h>
	int mkfifo(const char *pathname, mode_t mode);
2、内核会针对fifo文件开辟缓冲区,操作fifo文件,文件读写,实现通信
	注意:读会清空fifo内容
		  没有读,写自动关闭
		  读写必须同时进行,否则阻塞。

open注意事项:打开fifo文件时,read端会阻塞等待write端open,write端同理,
也会阻塞等待另一端打开
man 7 fifo
The kernel maintains exactly one pipe object for each FIFO special file that is
opened by at least one process.  The FIFO must be opened on both ends  (reading
and  writing)  before  data  can  be passed.  Normally, opening the FIFO blocks
until the other end is opened also.
3、删除命名管道
	unlink(pathname);
写端
	#include<stdio.h>
	#include<string.h>
	#include<stdlib.h>
	#include<sys/types.h>
	#include<sys/stat.h>
	#include<fcntl.h>
	#include<error.h>
	int main()
	{
		/* 	
			cmd
			mkfifo fifo  
			chmod 664 fifo
		*/
		int fd = open("fifo", O_WRONLY);
		if(fd < 0)
			perror("open error");
		ftruncate(fd, 16);
		char buf[16] = {0};
		int num = 1;
		while(1)
		{
			sprintf(buf, "Hello --%03d", num++);		
			int ret = write(fd, buf, strlen(buf));
			printf("OK ret = %d\n", ret);	
			sleep(1);
			if(num > 15)
				break;
		}
		printf("exit\n");
		close(fd);
		return 0 ;
	}
读端:
	#include<stdio.h>
	#include<string.h>
	#include<stdlib.h>
	#include<sys/types.h>
	#include<sys/stat.h>
	#include<fcntl.h>

	int main()
	{
		int fd = open("fifo", O_RDONLY);
		ftruncate(fd, 16);
		char buf[16] = {0};
		int num = 1;
		while(1)
		{
			memset(buf, 0, 16);
			int ret = read(fd, buf, sizeof(buf));
			if(ret > 0)
				printf("%s\n", buf);
			else
			{
				//ret = 0 退出
				printf("ret = %d\n", ret);
				break;
			}
			sleep(1);
		}
		printf("exit\n");
		close(fd);
		return 0 ;
	}
}

3、mmap(内存映射, 速度最快)
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
		  int fd, off_t offset);
	prot:PROT_READ  PROT_WRITE
    flags:MAP_SHARED(共享) 对文件的修改会影响到原文件
		   MAP_PRIVATE(私有) 进程要通信必须设置为MAP_SHARED
	fd 文件描述符,open打开的文件, -1 表示不是文件
	offset 偏移量
	返回值:成功返回可用的首地址内存
			失败返回  MAP_FAILED
int munmap(void *addr, size_t length);
	addr传mmap的返回值,length内存大小
	返回值:成功返回0,失败返回-1并设置错误号

问题:
文件的大小对映射区操作有影响,尽量避免
文件偏移量必须是4k的整数倍
文件描述符先关闭 对men操作无影响
新创建的文件不可以,使用 ftruncate函数
open文件时选择O_WRONLY,对映射有影响,报错无权限
open文件时选择O_RDONLY,对映射有影响,报错无权限

// 匿名映射关键词 MAP_ANONYMOUS或者用MAP_ANON -1
struct voteinfo *voteArr = (struct voteinfo *)mmap(NULL, sizeof(struct voteinfo) *32, 
	PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
	
MAP_ANONYMOUS和MAP_ANON在有些unix系统没有
/dev/zero	可以无限大
/dev/null	文件放进去会自动删除,所以放一些映射的文件,最后不用关心删除问题

1、无血缘关系通信
	借助文件描述符
int main()
{
	int fd = open("test", O_RDWR|O_CREAT, 0664);
	char *str = mmap(NULL, 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

	//ftruncate(fd, 16);  有没有都可以
	if(str == MAP_FAILED)
	{
		perror("mmap error");
		exit(1);
	}
	int num = 0; 
	while(1)
	{
		//memset(str, 0, sizeof(str));
		sprintf(str, "hello -%03d", num++); //先清空在写入,有无memset一样
		printf("%s\n", str);
		sleep(1);
	}
	munmap(str, 16);
	close(fd);
	return 0 ;
}

int main()
{
	
	int fd = open("test", O_RDWR|O_CREAT, 0664);
	char *str = mmap(NULL, 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

	//ftruncate(fd, 16);
	while(1)
	{
		printf("%s\n", str);
		sleep(1);
	}
	munmap(str, 16);
	close(fd);
	return 0 ;
}
// 说明:
// 文件描述符fd起到的作用媒介,因为mmap映射的内存不同,操作同一个文件才会使进程通信

4、本地socket(最稳定)
5、信号(携带信息量最小)
特点:简单,不能携带大量信息,满足特定条件发生
产生机制:进程B发送给进程A,内核产生信号,内核处理
信号的产生:
	按键 ctrl+c ctrl+z ctrl+\
	调用函数 kill raise abort
	定时器 alarm, setitimer
	命令产生 kill
	硬件错误: 段错误, 浮点型错误(double/0), 总线错误
信号的状态:
	产生;递达(信号到达并处理完);未决(信号被阻塞)
信号的默认处理方式:
	忽略;执行;捕获(重点)
9,19号信号不能捕捉,不能忽略,甚至不能阻塞

阻塞信号集和未决信号集
阻塞信号相当于未决信号前的一堵墙,2号中断信号(ctrl+c)到达时阻塞信号集会把2号置1,
然后把未决信号集2号置1(1 表示未处理),这样就阻塞了信号
#include <sys/types.h>
#include <signal.h>
	kill函数:
	int kill(pid_t pid, int sig);
	raise函数:给自己发信号
	int raise(int sig);
	void abort(void); 给自己发送异常信号
	
#include <unistd.h>
	unsigned int alarm(unsigned int seconds);
	//定时杀死自己
	alarm(6); //6s后杀死进程
	sleep(3);
	
	ret = alarm(4)
	ret = 3;//上次alarm所剩余的事件 6 - 3
	
#include <sys/time.h>
	int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value);
	struct itimerval {
	   struct timeval it_interval; /* 周期信号 */ 
	   struct timeval it_value;    /* 相当于时钟信号 */
	};
	struct timeval {
	   long tv_sec;                /* seconds */
	   long tv_usec;               /* microseconds */
	};
	which:
	自然时间
	ITIMER_REAL     按实际时间计时,计时到达将给进程发送SIGALRM信号
	计算进程执行时间
	ITIMER_VIRTUAL  仅当进程执行时才进行计时。计时到达将发送SIGVTALRM信号给进程.
	进程执行时间+调度时间
    ITIMER_PROF     当进程执行时和系统为该进程执行动作时都计时。与ITIMER_VIR-TUAL是一对,
					该定时器经常用来统计进程在用户态和内核态花费的时间。
					计时到达将发送SIGPROF信号给进程.
	new_value:要设置的闹钟时间
	old_value:旧的闹钟时间,可以为NULL
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/time.h>
#include<signal.h>

void catch_sig(int num)
{
	printf("Catch %d Sig\n", num);
}

int main()
{
	//信号到来才会起作用,不阻塞;
	signal(SIGALRM, catch_sig);
	/* 第一次信号5秒后到,后面每3秒到一次*/
	struct itimerval timer = {{3, 0}, {5, 0}};
	setitimer(ITIMER_REAL, &timer, NULL);

	while(1)
	{
		printf("111\n");
		sleep(1);
	}
	return 0 ;
}
信号集
#include <signal.h>
// initializes the signal set given by set to empty,  with allsignals excluded from the set.
int sigemptyset(sigset_t *set);
//	initializes set to full, including all signals.
int sigfillset(sigset_t *set);
//	add and delete respectively signal signum from set
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
//	tests whether signum is a member of set.
int sigismember(const sigset_t *set, int signum);

信号捕捉
	#include <signal.h>
	1	typedef void (*sighandler_t)(int);
		void func(int) <-- 函数指针
		sighandler_t signal(int signum, sighandler_t handler);
	
	2	int sigaction(int signum, const struct sigaction *act,
                 struct sigaction *oldact);
	signum: 要捕捉的信号(见附录)
	act:传入动作
	struct sigaction {
		void     (*sa_handler)(int);
		void     (*sa_sigaction)(int, siginfo_t *, void *);
		sigset_t   sa_mask;		//临时屏蔽的信号集
		int        sa_flags;	//0:第一个函数指针,SA_SIGINFO:第二个函数指针
		void     (*sa_restorer)(void);  //无效
	};
	oldact用于恢复现场;取出后保存,恢复时,传给第二个参数
	临时信号集里的信号只是临时的,只能暂时屏蔽,屏蔽后在处理

利用SIGCHLD信号回收子进程
	子进程死后会发送SIGCHLD信号,系统默认忽略,我们通过捕获SIGCHLD来回收子进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>

void catch_sig(int num)	// 信号捕捉函数不能使用非 异步安全函数
{
	pid_t wpid;
	//回收多个子进程
	while( (wpid = waitpid(-1, NULL, WNOHANG)) > 0)
	{
		printf("Catch %d child\n", wpid);
	}
}
int main()
{
	struct sigaction act;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	act.sa_handler = catch_sig;
	/*
		设置阻塞信号集在创建进程前,否则,
		子进程死了都没有阻塞到
	*/
	sigset_t cu_set, oldset;
	sigemptyset(&cu_set);
	sigaddset(&cu_set, SIGCHLD);
	sigprocmask(SIG_BLOCK, &cu_set, &oldset);
	
	pid_t pid;
	int i = 0;
	for(;i < 5; ++i)
	{
		pid = fork();
		if(pid == 0)
			break;
	}
	if(i == 5)
	{
		sleep(2); /* 子进程早于捕获死,就会变僵尸,设置阻塞信号集来解决 */
		sigaction(SIGCHLD, &act, NULL);
		
		/* 解除阻塞 */
		sigprocmask(SIG_SETMASK, &oldset, NULL);
		//sigprocmask(SIG_UNBLOCK, &cu_set, NULL);
		while(1)
		{
			sleep(1);
		}
	}
	if(i < 5)
	{
		printf("I am Child %d, pid = %d\n", i, getpid());
	}
	
	return 0;
}
6、共享内存
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */

int shm_open(const char *name, int oflag, mode_t mode);	//shm_open会创建并打开一个新的或者已经存在的
return:成功返回非负整形值,失败返回-1
name:路径,必须是根目录下如 /memory;
flag:O_RDONLY O_WRONLY O_RDWR
	非必须:O_CREAT: 创建一个不存在的共享内存对象;
			O_EXCL:如果O_CREAT被明确规定,如果已经存在对象会返回错误 EEXIST
			O_TRUNC:如果对象存在,将其截断为零字节。
mode:
S_IRUSR: 允许文件的所有者去读
S_IWUSR: 允许文件的所有者去写
S_IRGRP: 允许文件所有者的组去读
S_IWGRP: 允许文件所有者的组去写

int shm_unlink(const char *name);
gcc -lrt
7、消息队列
// 匿名管道是跟随进程的,消息队列是跟随内核的,也就是说进程结束之后,匿名管道就死了,但是消息队列还会存在(除非显示调用函数销毁)
// 管道是文件,存放在磁盘上,访问速度慢,消息队列是数据结构,存放在内存,访问速度快
// 管道是数据流式存取,消息队列是数据块式存取
1、创建一个消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
第一个参数是一个标识数据结构唯一的key键值,可以给IPC_PRIVATE让内核自动给,也可以自己调用ftok函数绑定一个
第二个参数是创建消息队列的参数,有IPC_CREAT 和 IPC_EXCL
	单独使用IPC_CREAT,如果该消息队列已经存在(就是该key_t对象已经拿去被创建过一个队列了),打开该队列并返回,如果不存在,就创建一个返回
	单独使用IPC_EXCL没有意义
	两个参数一起使用(IPC_CREAT | IPC_EXCL),如果该队列存在,出错返回,如果不存在创建一个返回,也就是说这样使用一定会获得一个新队列
返回值,成功返回标志消息队列的唯一的一个int,失败返回-1
1.2、获取key值
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname,int proj_id)
// 第一个是路径,第二个非0,最大占8位
// 返回值:成功返回key_t, 失败返回-1并且设置错误号
// ipcs -q 查看消息队列的状态
// ipcrm -q msqid 销毁一个消息队列
1.3、销毁一个消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid ,int cmd ,struct msgid_ds *buf)
// 第一个参数就是创建好的标识msg的int变量
// 第二个参数有很多,这里介绍两个,先说主要的销毁,IPC_RMID,设置了这个之后第三个参数就没必要设置了给个0就成
// 第二个参数设置成IPC_SET的时候表示对消息队列进行初始化,这时第三个参数就有用了
// 返回值:成功返回0,失败返回-1并设置错误号
struct msqid_ds {
	struct ipc_perm msg_perm;     /* Ownership and permissions */
	time_t          msg_stime;    /* Time of last msgsnd(2) */
	time_t          msg_rtime;    /* Time of last msgrcv(2) */
	time_t          msg_ctime;    /* Time of last change */
	unsigned long   __msg_cbytes; /* Current number of bytes in
										queue (nonstandard) */
	msgqnum_t       msg_qnum;     /* Current number of messages
										in queue */
	msglen_t        msg_qbytes;   /* Maximum number of bytes
										allowed in queue */
	pid_t           msg_lspid;    /* PID of last msgsnd(2) */
	pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};
The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):
struct ipc_perm {
	key_t          __key;       /* Key supplied to msgget(2) */
	uid_t          uid;         /* Effective UID of owner */
	gid_t          gid;         /* Effective GID of owner */
	uid_t          cuid;        /* Effective UID of creator */
	gid_t          cgid;        /* Effective GID of creator */
	unsigned short mode;        /* Permissions */
	unsigned short __seq;       /* Sequence number */
};
2、使用消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
2.1、发送消息
int msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg)
// 第一个还是消息队列号
// 第二个是一个结构体的指针,这个结构体叫做msgbuf在内核中有(应该是有的。。。没有就自己写一个放程序里),必要的,不能设置为0
// 第三个就是传输的消息长度(一般你的数据有多长就写多长,不是msgbuf对象的总大小)
// 第四个是传送方式(无阻塞啊什么的)的参数,没有使用设置为0
struct msgbuf {
	long mtype;       /* message type, must be > 0 */
	char mtext[1];    /* message data 自己写可以把大小变大*/
};
2.2、接收消息
ssize_t msgrcv(int msgid,const void *msgp,size_t msgsz,long msgtyp,int msgflg)
msgtyp就是用来表示我(当前进程)拿数据的时候,只拿(msgbuf对象中的mtype)和我传入的msgtyp一样的数据块(msgbuf对象)
8、进程间同步问题
信号量(semaphore)是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
1、特点
信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
支持信号量组。

无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。
2、原型
最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。
而可以取多个正整数的信号量被称为通用信号量。

Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

// 信号量系统调用
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

key_t ftok(const char *pathname, int proj_id);
/**
 * ftok 函数取pathname指向的文件的索引节点号,proj_id值可以任意给,但是只取低8位
 * 索引节点类似结构体,内部包含文件的拥有者,文件的group ID,文件的读写可执行权限,文件的时间戳等等。而索引节点号唯一标识该索引节点
 * 故linux内核给每个文件都分配一个索引节点号,所以存在索引节点号创建完毕而导致无法创建文件的情况 (ls -i查看索引节点号)
 * linux内核查找文件都是根据索引节点号查找,名字和节点号相当于一对一映射
 */
// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf *sops, size_t nsops);	// semid为semget返回值
// 时间内设置信号量的值
int semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout);
// 控制信号量的相关信息, 包括删除信号量
int semctl(int semid, int sem_num, int cmd, ...);

struct sembuf
{
	unsigned short int sem_num;	/* semaphore number */		// 除非使用一组信号量,否则它为0 
	short int sem_op;			/* semaphore operation */ 	// 取1(V)或-1(P)
	short int sem_flg;			/* operation flag */		// 通常为SEM_UNDO,使操作系统跟踪信号量,并在进程没有释放该信号量而终止时,操作系统释放信号量 
};

3、有名/无名信号量
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
1、有名信号量
// 创建一个sem_t
sem_t *sem_open(const char *name, int oflag);	// 当文件存在时使用此方法
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); // 当文件不存在时使用此方法
参数:
	name: 文件名,无需加路径,默认路径在/dev/shm;
	oflag: 一般 O_CREAT|O_RDWR
	mode: 权限,8进制,一般0664
	value: 初始值
返回值:成功返回信号量地址,失败返回SEM_FAILED;
/**
 * sem_open详细说明
 * 创建或打开一个已存在的信号量,由name来唯一标识(实质是索引节点号)
 * oflag携带了O_CREAT,如果文件不存在则需指定mode和value,如果此文件已存在,则后面两个参数忽略(但必须存在)
 * 如果oflag同时携带了O_EXCL,那么当文件存在时会返回错误
 */


// 关闭有名信号量
int sem_close(sem_t *sem);
// 删除有名信号量文件
int sem_unlink(const char *name);
成功返回0,失败返回-1

2、无名信号量
//初始化信号量,在sem指向的地址初始化未命名信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
/*
 * sem: 指向共享内存的地址
 * pshared: 为0表示这个信号量共享给一个进程中多个线程
 *		   大于0表示这个信号量共享给多个进程
 * value:指定信号量的初始值
 * return: 0成功,-1错误
 * 错误号:EINVAL:value超过了SEM_VALUE_MAX; 
 *         ENOSYS:pshared非0,但系统不支持进程共享信号量
 */

/*
* 减少 *sem 的值,如果为0则阻塞等待 *sem 为大于0后在减少
* 如果大于0则减少后立即返回,成功返回0,失败返回-1并设置错误号
* 错误号:EAGAIN表示当前 *sem 为0;
*/
int sem_wait(sem_t *sem);
/*
* 和sem_wait相似,但是如果不能执行减少,会立即返回错误并设置错误号为 EAGAIN
*/
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

//解锁sem,增加 *sem 的值
//如果信号量的值因此变得大于零  
//然后,在sem_wait调用中阻塞的另一个进程或线程将被唤醒并继续锁定信号量。
int sem_post(sem_t *sem);

// 无名信号量销毁,只能销毁由sem_init初始化的sem_t
int sem_destroy(sem_t *sem);
gcc -lpthread
9、信号附录
  1. SIGHUP 02) SIGINT 03) SIGQUIT 04) SIGILL 05) SIGTRAP
  2. SIGABRT 07) SIGBUS 08) SIGFPE 09) SIGKILL 10) SIGUSR1
  3. SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
  4. SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
  5. SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
  6. SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
  7. SIGSYS
9号和19号无法捕获
ctrl z是19号SIGSTOP信号,需使用18号SIGCONT继续让进程运行,或者使用bg(后台运行), fg(前台运行)

10、可以杀死进程的信号 kill -num pid
	1 -> SIGHUP
	2 -> SIGINT
	3 -> SIGQUIT
	4 -> SIGILL
	5 -> SIGTRAP
	6 -> SIGABRT
	7 -> SIGBUS
	8 -> SIGFPE
	9 -> SIGKILL

SIGHUP 重要
	// 本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。
	// 登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,
	// 一般都属于这个 Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。
	// 这个信号的默认操作为终止进程,因此前台进 程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,
	// 比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也 能继续下载。
	// 此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
SIGINT 重要
	程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。
SIGQUIT 重要
	和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
SIGILL
	执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
SIGTRAP
	由断点指令或其它trap指令产生. 由debugger使用。
SIGABRT
	调用abort函数生成的信号。
SIGBUS
	非法地址, 包括内存地址对齐(alignment)出错。
	比如访问一个四个字长的整数, 但其地址不是4的倍数。
	它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。
SIGFPE
	在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
11、exit(0)和_exit(0)区别
exit(0)是通过_exit(0)来实现的,其次,exit(0)会执行一些后续的处理,比如把缓冲区中的内容写到磁盘的相应位置上,关闭没有关闭的文件......而_exit(0)不会做这些事情。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值