本文共 26561 字,大约阅读时间需要 88 分钟。
目录
进程:动态概念(运行中程序)
程序:静态(一些指令集合)程序文件(可以移动) 软件:文档加程序 进程:进程是系统分配资源的最小单位 进程控制(进程状态)动态--生命周期 创建--调度--销毁pid_t fork(void)
返回值 <0创建失败 ==0现在所处的进程是子进程 >0 现在所处的进程是父进程(子进程的id号) fork的两种用法 (1)一个父进程希望复制自己,使父,子进程同时执行不同代码段。这在网络服务进程中是常见的--父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则等待下一个服务请求到达 (2)一个进程执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec每一次fork都会产生一个子进程,而wait(),相当于递归一样,等待最后一个子进程死后才去打印它上一个进程:
#includevfork---先运行子进程,(子进程退出后)运行父进程#include #include #include #include int main(int argc, char **argv){ pid_t x; int i; for(i=0; i<10; i++) { x = fork(); if(x > 0) break; if(x == 0) continue; } wait(NULL); printf("PID %d PPID %d\n", getpid(), getppid()); return 0;}
注意:一定要在子进程里要用exit()来退出子进程,不然会进入无限的循环。
fork()与vfork()的区别~~~:>>主要为两点:
(1)执行次序:fork():对父子进程的调度室由调度器决定的;
vfork():是先调用子进程,等子进程的exit(1)被调用后,再调用父进程;
(2)对数据段的影响:fork():父子进程不共享一段地址空间,修改子进程,父进程的内容并不会受影响。
vfork():在子进程调用exit之前,它在父进程的空间中运行,也就是说会更改父进程的数据段、 栈和堆。。即共享代码区和数据区,且地址和内容都是一样的。
每一个进程都有一个非负数整型表示的唯一ID。
getpid()--获取当前进程的id号 getppid()--获取当前进程的父进程id getuid()--调用进程用户的实际ID geteuid()--调用进程的有效用户ID getgid()---调用进程实际组ID getegid()--调用进程的有效组ID父进程还运行子进程已经退出(但是父进程没有回收子进程资源)(编程时候必须避免)
父进程回收子进程资源 在父进程中调用wait pid_t wait(int *status)父进程已经退出子进程还在运行(最后都会把init进程作为父进程)
操作过程:在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正在终止的子进程,如果是,则将该进程的父进程ID更改为1(init的进程ID)int execl(const char *path, const char *arg, ...); 后面变参必须以NULL结束(类似于main函数参数) execl("/bin/ls", "ls","-a", "-l", NULL); ls -a -l 必须把要运行的程序路径加到PATH环境变中 int execlp(const char *file, const char *arg, ...); execl("ls", "ls","-a", "-l", NULL); ls -a -l int execle(const char *path, const char *arg,..., char * const envp[]); char *envp[] = {"MM=/home/gec", "/usr/local", NULL}; execle("/home/gec/myshare/main", "main", NULL, envp); int execv(const char *path, char *const argv[]); char *argv[] = {"ls","-a", "-l", NULL}; execv("/bin/ls", argv); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[],char *const envp[]); system("/home/gec/main")---类似于函数调用
exec函数族成功执行后,原有的程序代码都将被指定的文件或脚本覆盖,,后面的代码无法运行,也无法返回
是指在后台运行且不与终端相连街的一种进程(也称之为精灵进程或后台进程)
#ifndef __DAEMON_H#define __DAEMON_H#include#include #include #include #include int daemon_init(void);#endif
daemon.c
#include "daemon.h" int daemon_init(void){ pid_t pid; int fd0, fd1, fd2, max_fd,i; // 1 if((pid = fork()) < 0) { perror("fork faild!"); exit(1); } else if(pid != 0)//让父进程能出 { exit(0); } // 2 signal(SIGHUP, SIG_IGN);//忽略终端被关闭的信号 // 3 if(setsid() < 0) //创建一个新的会话 { exit(1); } // 4 if((pid = fork()) < 0) { perror("fork faild!"); exit(1); } else if(pid != 0) exit(0); // 5 setpgrp(); //设置孙子进程的进程组,彻底脱离会话 // 6 关闭看有的文件描述符,默认是1024个,但没有那么多打开,那么close就没反应的 max_fd = sysconf(_SC_OPEN_MAX);//获取文件打开的最大个数 for (i = max_fd; i>=0; i--) { close(i);//关闭文件 } // 7 ; 0:表示不会影响你设置的权限,当你是umask(0777)的话,你要设置的文件权限都会被减为0,没有权限的意思 umask(0); // 8 修改守护进程的当前工作路径,让它挂载到跟目录不会被卸载 chdir("/"); //Initialize the log file. 打开日记文件 openlog("daemon_test", LOG_CONS | LOG_PID, LOG_DAEMON); return 0;}main.c
#include#include "daemon.h"int main(void){ daemon_init(); //初始化一个精灵进程 while(1) { syslog(LOG_DAEMON, "I am a daemonAAA!"); //将这句话写到日记文件里 sleep(2); //2S写一次,但日记里只会出现一次,下面会告诉你重复了多少次 } return 0;}
进程间通信 管道, 信号, 消息队列,信号量,内存共享
无名管道--只使用于亲属进程(父子进程)
PIPE的特征: 1、没有名字,因此无法使用open() (只能在一个进程中被创建出来,通过继承方式将它的文件描述符传递给子进程,这也是 PIPE只能用于亲缘进程的原因) 2、 只能用于亲缘进程间的通信(父子进程,兄弟进程,祖孙进程) 3、 半双工工作方式:读写端分开 4、 写入操作不具有原子性,因此只能用于一对一的简单通信情型 5、 不能使用lseek()来定位(因为它的数据不像普通文件那样按块的方式存放在如硬盘,Flash等块设备上)#include#include #include #include #include int main(int argc, char **argv){ int fd[2];//用于存放PIPE的两个文件描述符 int ret = pipe(fd); if(ret < 0) { perror("create pipe failed \n"); return -1; } pid_t pid = fork(); if(pid < 0) { perror("create fork failed\n"); } if(pid == 0) { write(fd[1],"hello world",12); } if(pid > 0) { char buf[12] = {0}; read(fd[0],buf,12); printf("%s\n",buf); } close(fd[0]); close(fd[1]); return 0;}
#includepipe3.c#include #include #include #include #include #include int main(int argc, char **argv){ int processed; int fd[2]; const char data[] = "123"; char buf[BUFSIZ+1]; memset(buf,'\0',sizeof(buf)); int ret = pipe(fd); if(ret < 0) { perror("create pipe failed\n"); return -1; } pid_t pid = fork(); if(pid < 0) { perror("create fork failed\n"); return -1; } if(pid == 0) { //把读取管道数据的文件描述符保存到一个缓存区中, //该缓存区的内容将构成pipe3程序的一个参数 sprintf(buf,"%d",fd[0]); //(char *)0的作用是终止被调用程序的参数列表 (void)execl("pipe3","pipe3",buf,(char *)0); exit(EXIT_FAILURE); } if(pid > 0) { processed = write(fd[1],data,strlen(data)); printf("%d --wrote %d bytes\n",getpid(),processed); } return 0;}
#include#include #include #include #include #include int main(int argc, char **argv){ int processed; char buf[BUFSIZ+1]; int descriptor; memset(buf,'\0',sizeof(buf)); sscanf(argv[1],"%d",&descriptor); processed = read(descriptor,buf,BUFSIZ); printf("%d --read %d bytes: %s\n",getpid(),processed,buf); return 0;}
有一个实实在在的管道文件-
FIFO的特征 1、有名字,存储于普通文件系统之中 2、任何具有相应权限的进程都可以使用open()来获取FIFO的文件描述符(程序不能以O_RDWR模式打开FIFO文件进行读写操作) 3、跟普通文件一样,使统一的read()/write()来读/写 4、跟普通文件不同,不能使用lseek()来定位,原因同PIPE 5、具有写入原子性,支持多写者同时进行写操作而数据不会被践踏 6、 First in First out 最先被写入FIFO的数据,最先被读出 创建管道 mkfifo 文件名int mkfifo(const char *pathname, mode_t mode);
===》无数据===》立即返回
写操作write 有读者===》缓存未满===》正常写入 缓存已满===》阻塞等待 无读者===》缓存未满===》立即收到SIGPIPE ===》 缓存已满===》 立即收到SIGPIPE FIFO间通信代码 fifoA.c#includefifoB.c#include #include #include #include #include #define FIFOPATH "/tmp/fifo"int main(int argc, char **argv){ int ret = mkfifo(FIFOPATH, 00777); if(ret < 0 && errno != EEXIST) { perror("create fail"); return -1; } // int fd = open(FIFOPATH, O_RDWR); if(fd < 0) { perror("open fifo fail"); return -1; } char buf[32] = {0}; while(1) { scanf("%s", buf); ret = write(fd, buf, strlen(buf)+1); if(ret <= 0) { perror("write fail"); break; } if(strcmp(buf, "quit") == 0) break; } close(fd); return 0;}
#includepopen()和pclose() FILE *popen(const char *command, const char *type); popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据#include #include #include #include #include #define FIFOPATH "/tmp/fifo"int main(int argc, char **argv){ int ret = mkfifo(FIFOPATH, 00777); if(ret < 0 && errno != EEXIST) { perror("create fail"); return -1; } // int fd = open(FIFOPATH, O_RDWR); if(fd < 0) { perror("open fifo fail"); return -1; } char buf[32] = {0}; while(1) { ret = read(fd, buf, 32); if(ret <= 0) { perror("write fail"); break; } if(strcmp(buf, "quit") == 0) break; printf("wwww%s\n", buf); } close(fd); return 0;}
参数
command:字符串是要运行的程序名和相应的参数 type: “r”:被调用的程序输出就可以被调用程序使用,调用程序利用popen函数返回的FILE*文件流指针,就可以通过常用的stdio库函数 (如fread)来读取被调用程序的输出 “w”:调用程序就可以用fwrite嗲用向被调用程序发送数据,而被调用程序就可以在自己的标准输入上读取这些数据,被调用的程 序 通常不会意识到自己正从另一个进程读取数据,它只是在标准输入流上读取数据,然后做出相应的操作 int pclose(FILE *stream); 读取外部程序的输出:#include#include #include #include #include #include #include int main(int argc, char **argv){ FILE *read_fp; char buffer[BUFSIZ+1]; int chars_read; memset(buffer,'\0',sizeof(buffer)); read_fp = popen("ls -l","r"); if(read_fp != NULL) { chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp); if(chars_read > 0) { printf("output was:-\n%s\n",buffer); } pclose(read_fp); exit(EXIT_SUCCESS); } return 0;}
将读出送往外部程序:
#include#include #include #include #include #include #include int main(int argc, char **argv){ FILE *write_fp; char buffer[BUFSIZ+1]; sprintf(buffer,"Once upon a time,threr was...\n"); write_fp = popen("od -c","w"); if(write_fp != NULL) { fwrite(buffer,sizeof(char),strlen(buffer),write_fp); pclose(write_fp); exit(EXIT_SUCCESS); } return 0;}
无法在管道中读取一个指定的数据,因为这些数据没有做任何标记,读者进程只能按次序地逐个读取
发送和接受信号
kill()和signal() 发送信号kill: 信号拦截signal 1、 sighandler_t signal(int signum, sighandler_t handler);(拦截的信号, 拦截后要做的事情) 2、 typedef void (*sighandler_t)(int);(升级板)sigqueue()和sigaction()
struct sigaction的结构体
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); };代码:
void ouch(int sig){ printf("ouch %d",sig);}int main(int argc, char **argv){ struct sigaction act; act.sa_handler = ouch; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT,&act,NULL); while(1) { printf("hello world\n"); sleep(1); } return 0;}sigaction.c:
#include#include #include #include #include #include void f(int sig, siginfo_t *info, void *arg){ printf("sig: %d\n", sig); // SIGINT(2) printf("int: %d\n", info->si_int); // a.sival_int printf("from %d\n", info->si_pid);}int main(void){ pid_t x = fork(); if(x > 0) // 父进程 { struct sigaction act; bzero(&act, sizeof(act)); act.sa_sigaction = f; act.sa_flags = SA_SIGINFO; // 我要用扩展版的响应函数 // 向内核申请,将来收到SIGINT信号的时候,按照act结构体中 // 描述的情况来处理。NULL表示不需要内核返回该信号原来的处理模式 sigaction(SIGINT, &act, NULL); // signal()的扩展版 pause(); } if(x == 0) // 子进程 { union sigval a; a.sival_int = 100; printf("child PID: %d\n", getpid()); sleep(1); // 给父进程发送信号SIGINT,顺便还发了a这个值 sigqueue(getppid(), SIGINT, a); // kill()的扩展版 }}
p1:int main(int argc, char **argv){ printf("my PID %d\n",getpid()); pause(); return 0;}p2:int main(int argc, char **argv){ pid_t pid; scanf("%d",&pid); kill(pid,SIGINT); return 0;}通过p2发送信号将p1杀死
p1:int main(int argc, char **argv){ printf("my PID %d\n",getpid()); signal(SIGINT,SIG_IGN); pause(); return 0;}p2:int main(int argc, char **argv){ pid_t pid; scanf("%d",&pid); kill(pid,SIGINT); return 0;}p2向p1发送杀死信号,没有反应,因为p1已经忽略该信号 SIGKILL和SIGSTOP都无法忽略
3、捕捉
p1:void f(int sig){ printf("catch sig:%d\n",sig);}int main(int argc, char **argv){ printf("my PID %d\n",getpid()); signal(SIGINT,f); pause(); return 0;}p2:int main(int argc, char **argv){ pid_t pid; scanf("%d",&pid); kill(pid,SIGINT); return 0;}f()函数由内核调用,函数预先告诉内核,等接受到SIGINT信号时,调用f()函数4、阻塞 代码
void catch(int sig){ fprintf(stderr,"%d catch SIGQUIT\n",getpid());}int main(int argc, char **argv){ signal(SIGQUIT,catch); sigset_t sig; sigemptyset(&sig); sigaddset(&sig,SIGQUIT); sigprocmask(SIG_BLOCK,&sig,NULL); pid_t pid; pid = fork(); if(pid == 0) { fprintf(stderr,"child %d\n",getpid()); int i = 10; while(i>0) { printf("%d\n",i--); sleep(1); } sigprocmask(SIG_UNBLOCK,&sig,NULL); while(1) pause(); } if(pid > 0) { fprintf(stderr,"parent %d\n",getpid()); sigprocmask(SIG_UNBLOCK,&sig,NULL); while(1) pause(); } return 0;}
因为SIGQUIT被阻塞了,子进程接收到SIGQUIT是没到反应,等到10秒后解除阻塞才有反应,而父进程一开始就已经解除阻塞
其他
查看或删除系统中的IPC对象 获取一个当前未用的IPC的key消息队列提供一种带有数据标识的特殊管道,使用一段被写入的数据都变成带标识的消息,读取该段消息的进程只要指定这个标识就可以正确的读取,而不受到其他消息的干扰
(1)发送者
a、 获取消息队列的ID int msgget(key_t key, int msgflg); b、 将数据放入一个附带有标识的特殊的结构体,发送给消息队列 (2)接收者 A、 获取消息队列的ID B、将指定标识的消息读出 (3 )设置或获取消息队列的相关属性 1、int msgctl(int msqid, int cmd, struct msqid_ds *buf); 2、msqid:消息队列ID 3、cmd: IPC_STAT:获取该MSG的信息,存储在结构体msqid_ds中 IPC_SET:设置该MSG的信息,储存在结构体msqid_ds中 IPC_RMID:立即删除该MSG,并唤醒所有阻塞在该MSG上的进程,同时忽略第三个参数 IPC_INFO:获取关于当前系统中MSG的限制值信息 MSG_INFO:获取关于当前系统中MSG的相关资源消耗信息 MSG_STAT:同IPC_STAT,但msgid为该消息队列的内核中记录所有信息的数组的下标,因此通过迭代所有的下标可以获得系统 中所有消息队列的相关信息 4、buf:相关信息结构提的缓冲区 5、IPC_STAT获取的属性信息被存放在一下结构体中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) */ };
6、其中,权限相关的信息用如下结构体来表示
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 */ };(4)删除消息队列 msgctl(msgid,IPC_RMID,NULL);
1.设定一个key值
2.在内核空间中申请队列(id) 3.写数据 4.读数据 5.销毁多个进程可以把一段内存映射到自己的进程空间,以此来实现数据的共享以及传输,这也是所有进程间通信方式中最快的一种
struct shmid_ds { struct ipc_perm shm_perm; /* Ownership and permissions */ size_t shm_segsz; /* Size of segment (bytes) */ time_t shm_atime; /* Last attach time */ time_t shm_dtime; /* Last detach time */ time_t shm_ctime; /* Last change time */ pid_t shm_cpid; /* PID of creator */ pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */ shmatt_t shm_nattch; /* No. of current attaches */ ... };
其中权限信息结构体:
struct ipc_perm { key_t __key; /* Key supplied to shmget(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 + SHM_DEST and SHM_LOCKED flags */ unsigned short __seq; /* Sequence number */ };5、可以通过shmctl获取或设置共享内存的相关属性 int shmctl(int shmid, int cmd, struct shmid_ds *buf); shmid:共享内存的ID cmd:要采取的动作 常用的值 IPC_STAT:获取属性信息,放到buf中
struct shmid_ds { struct ipc_perm shm_perm; /* Ownership and permissions */ size_t shm_segsz; /* Size of segment (bytes) */ time_t shm_atime; /* Last attach time */ time_t shm_dtime; /* Last detach time */ time_t shm_ctime; /* Last change time */ pid_t shm_cpid; /* PID of creator */ pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */ shmatt_t shm_nattch; /* No. of current attaches */ ... };IPC_SET:设置属性为buf指向的内容 IPC_RMID:删除共享内存段 buf:属性信息结构体指针 代码: shmA.c
#includeshmB.c#include #include #include #include #include #include #include #include int main(int argc, char **argv){ //1.获取key值 key_t key = ftok("/", 100); printf("key = %d\n", key); //2.获取共享内存id int shmid = shmget(key, 1024, IPC_CREAT|0777); if(shmid < 0) { perror("get id fail"); return -1; } //3.映射--链接 char *p = shmat(shmid, NULL, 0); memset(p, 0, 1024); memcpy(p, "hello haha", 11); getchar(); //4.释放映射 shmdt(p); //5.获取共内存状态 struct shmid_ds shmbuf; int ret = shmctl(shmid, IPC_STAT, &shmbuf); if(ret < 0) { perror("获取状态失败"); } //当映射该SHM的进程个数为0时,删除共享内存 if(shmbuf.shm_nattch == 0) { shmctl(shmid, IPC_RMID, NULL); } return 0;}
#include#include #include #include #include #include #include #include #include int main(int argc, char **argv){ //1.获取key值 key_t key = ftok("/", 100); printf("key = %d\n", key); //2.获取共享内存id int shmid = shmget(key, 1024, IPC_CREAT|0777); if(shmid < 0) { perror("get id fail"); return -1; } //3.映射--链接 char *p = shmat(shmid, NULL, 0); if(p == NULL) { perror("shmat fail"); } printf("p=%s", p); getchar();}
1、概念
信号量的P,V操作最核心的特征是:它们是原子性的,也就是说对信号量元素的值的增加和减少,系统保证CUP的电气特性级别上不可分割,这跟整型数据的加减法有本质的区别
2、 获取信号量的ID int semget(key_t key, int nsems, int semflg); key:信号量的键值 nsems:信号量的个数 semflg: IPC_CREAT:如果key对应的信号量不存在,则创建 IPC_EXCL:如果key对应的信号量存在,则报错 mode:信号量的访问权限 3、对信号量进行P/V操作,或等零操作 int semop(int semid, struct sembuf *sops, unsigned nsops); semid:信号量的ID(是由semget返回的信号量标识符) sops:信号量操作结构体数组struct sembuf{ unsigned short sem_num; /* 信号量的元素序号(数组下标)(除非需要使用一组信号量,否则它的取值一般为0) */ short sem_op; /* 操作元素(通常只会用到两个值,一个是-1,也就是P操作,它等待信号量变为可用,一个是+1,也就是V操作,它发送信号表示信号量现在已可用) */ short sem_flg; /* 操作选项 */}
nsops:结构体数组元素个数
4、 获取或设置信号量的相关信息 int semctl(int semid, int semnum, int cmd, ...); semid:信号量ID semmum:信号量元素序号(当需要用到成组的信号量是,就需要用到这个数,它一般取值为0,表示这是第一个也是唯一一个信号量) cmd:将要采取的动作 两个常用的值 SETVAL:用来把信号量初始化为一个已知的值,这是通过union semun中的val成员设置,其作用是咋信号量第一次使用之前对它进行设置union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ };
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符
线程是系统调度的最小单位。
进程资源(进程间数据独立) 线程资源(一个进程中的多个子线程是共用进程资源)(数据段, 堆)线程有自己的独立栈空间
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
thread:新线程的TID 用pthread_t ID名来创建 attr:线程属性(线程属性如果为NULL,则会创建一个标准属性的线程,) start_routine:线程例程(线程例程指的是如果线程创建成功,那么该线程会立即去执行的函数) arg:线程的例程参数 返回值 成功:0 失败:errno 代码struct student{ char name[20]; int id; int phone;};void * function(void *arg){ struct student *al = ((struct student *)arg); while(1) { printf("name =%s id = %d phone = %d\n",al->name,al->id,al->phone); sleep(1); }}int main(int argc, char **argv){ pthread_t pthread; struct student arg; strcpy(arg.name,"wangyisi"); arg.id = 883; arg.phone = 123456; int ret = pthread_create(&pthread,NULL,function,(void *)&arg); if(ret !=0) { perror("pthread_ceate fail\n"); exit(1); } while(1) { printf("wait----\n"); sleep(1); } return 0;}
(1)定义线程属性变量,并使用pthread_attr_init初始化
int pthread_attr_init(pthread_attr_t *attr); (2)使用pthread_attr_setXXX来设置相关的属性 (3)使用该线程属性变量创建相应的线程 (4)使用pthread_attr_destroy()销毁该线程属性变量 int pthread_attr_destroy(pthread_attr_t *attr);1、一条线程如果可接合,意味着这条线程在退出时不会自动释放自身资源,而会成为僵尸进程,同时意味着该线程退出值可以 被 其他线程取代,因此,如果不需要某条线程的退出值,那最好将线程设置为分离状态以保证该线程不会成为僵尸线程
2、pthread_detach(id); 3、pthread_attr_setdetachstate 参数 attr:线程属性变量 detachstate PTHREAD_CREATE_DETACHED:分离 PTHREAD_CREATE_JOINABLE:接合代码
#include#include void *routine(void *arg){ pthread_exit(NULL);}int main(int argc, char **argv){ pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_t tid; int i=0; for(i=0; i<10; i++) { pthread_create(&tid, &attr, routine, NULL); } pause(); return 0;}
void pthread_exit(void *retval);
retval:线程退出值(1)int pthread_cancel(pthread_t thread);
给指定的线程发送一条取消请求 (2)thread:线程TID (3)设置取消状态 int pthread_setcancelstate(int state, int *oldstate); int pthread_setcanceltype(int type, int *oldtype); 参数 state新的取消状态 PTHREAD_CANCEL_ENABLE:使能取消请求 PTHREAD_CANCEL_DISABLE:关闭取消请求 oldstate:旧的取消请求 type:新的取消类型 PTHREAD_CANCEL_DEFERRED:延时响应 PTHREAD_CANCEL_ASYNCHRONOUS:立即响应 oldtype:旧的取消类型int pthread_join(pthread_t thread, void **retval);
thread:线程TID retval:储存线程退出值的内存的指针 该函数要注意的几点 如果线程退出是没有退出值,retval可以指定为NULL pthread_join()指定的线程如果还在运行,那么它将会阻塞等待 代码1#include代码2(将线程里的值返回到线程)#include #include #include #include #include #include void * function(void *arg){ int i = 0; while(i < 10) { printf("%d \n",i++); sleep(1); }}int main(int argc, char **argv){ pthread_t pthread; int res = pthread_create(&pthread,NULL,function,NULL); if(res != 0) { perror("create failed\n"); exit(1); } pthread_join(pthread,NULL); printf("over\n"); return 0;}
#include#include void *routine(void *arg){ char *s = (char *)arg; printf("argument: %s", s); sleep(1); pthread_exit("Bye-Bye!\n");}int main(int argc, char **argv){ pthread_t tid; pthread_create(&tid, NULL, routine, (void *)"testing string\n"); void *p; pthread_join(tid, &p); printf("exit value: %s", (char *)p); return 0;}
sudo apt-get install manpsges sudo apt-get install manpsges manpsges-dev manpages-posix-dev
int pthread_mutex_init (pthread_mutex_t *__mutex,const pthread_mutexattr_t *__mutexattr)
int pthread_mutex_destroy (pthread_mutex_t *__mutex)
int pthread_mutex_trylock (pthread_mutex_t *__mutex)
int pthread_mutex_lock (pthread_mutex_t *__mutex)
int pthread_mutex_unlock (pthread_mutex_t *__mutex)
(7)代码
#include#include #include #include #include #include #include pthread_mutex_t m;void * function(void *arg){ char *msg = (char *)arg; pthread_mutex_lock(&m); while(*msg != '\0') { fprintf(stderr,"%c",*msg); usleep(100); msg++; } pthread_mutex_unlock(&m); pthread_exit(NULL);}int main(int argc, char **argv){ pthread_mutex_init(&m,NULL); pthread_t t1,t2; pthread_create(&t1,NULL,function,"AAAAAAAAAAAAAAAAAAAAA"); pthread_create(&t2,NULL,function,"BBBBBBBBBBBBBBBBBBBBB"); pthread_exit(NULL); return 0;}
POSIX有名信号量使用步骤
(1)使用sem_open()来创建或打开一个有名信号量 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:信号量的名字,必须翼正斜杠“/”开头 oflag: O_CREATE:如果该名字对应的信号量不存在,则创建 O_EXCL:如果该名字对应的信号量已存在,则报错 mode:八进制读写权限,0666 value:初始化 (2)使用sem_wait()和sem_post()来分别进行P操作和V操作 int sem_wait(sem_t *sem);将信号量的值减1 int sem_post(sem_t *sem);将信号量的值加1 (3)使用sem_close()来关闭它 int sem_close(sem_t *sem); sem:信号量指针 (4)使用sem_unlink()来删除它,并释放系统资源 int sem_unlink(const char *name); name:信号量名字#include#include #include #include #include #include #include #include #include sem_t space,data;void * function(void *arg){ char *buf = (char *)arg; while(1) { sem_wait(&data);//等待有数据,就往里面读 printf("bytes:%d\n",strlen(buf)); sem_post(&space);//读完后空间就加1 }}int main(int argc, char **argv){ sem_init(&space,0,1); sem_init(&data,0,0); char buf[32]; pthread_t tid; pthread_create(&tid,NULL,function,(void *)buf); while(1) { sem_wait(&space);//等待有空间就往内存写数据 bzero(buf,32); fgets(buf,32,stdin); sem_post(&data);//数据就加1 } return 0;}
进程间通信用到同步用到有名信号量,线程间通信同步用到匿名信号量
转载地址:http://dezwi.baihongyu.com/