一、wait和waitpid区别:
1.在一个子进程终止前,wait使调用者阻塞
2.waitpid有一个选择项,可以使调用者不阻塞
3waitpid可以等待指定的一个子进程,wait等待所有的子进程,返回任意一个终止的子进程状态。
子进程在运行中有暂停信号,如果想要显示暂停信号的信号码不能使用wait()要用waitpid()
4、waitpid的返回值比wait稍微复杂一些,一共有3种情况:
(1) 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
(2)如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
(3)如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
二、SIGNAL信号:子进程结束时, 父进程会收到这个信号。
signal(参数一,参数二)
- 参数一:我们要进行处理的信号。系统的信号我们可以再终端键入 kill -l查看(共64个)。其实这些信号时系统定义的宏。
- 参数二:我们处理的方式(是系统默认还是忽略还是捕获)。可以写一个handdle函数来处理我们捕获的信号。
- SIGING:忽略信号
三、Linux下DIR,dirent,stat等结构体详解
1、DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构保存当前正在被读取的目录的有关信息,
函数 DIR *opendir(const char *pathname),即打开文件目录,返回的就是指向DIR结构体的指针 ,
而该指针由以下几个函数使用:
- struct dirent *readdir(DIR *dp);
- void rewinddir(DIR *dp);
- int closedir(DIR *dp);
- long telldir(DIR *dp);
- void seekdir(DIR *dp,long loc);
2、dirent结构体
首先我们要弄清楚目录文件(directory file)的概念:这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针 ,
从定义能够看出,dirent不仅仅指向目录,还指向目录中的具体文件,readdir函数同样也读取目录下的文件,这就是证据。以下为dirent结构体的定义:
- {
- long d_ino; /* inode number 索引节点号 */
- off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
- unsigned short d_reclen; /* length of this d_name 文件名长 */
- unsigned char d_type; /* the type of d_name 文件类型 */
- char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
- }
3、获取某目录下(比如a目下)b文件的详细信息
首先,我们使用opendir函数打开目录a,返回指向目录a的DIR结构体c。
接着,我们调用readdir( c)函数读取目录a下所有文件(包括目录),返回指向目录a下所有文件的dirent结构体d。
然后,我们遍历d,调用stat(d->name,stat *e)来获取每个文件的详细信息,存储在stat结构体e中。
四、fork()创建进程,子进程一直进不去
***父进程和子进程执行顺序不确定。
可能是父进程先执行然后结束了,如果父进程执行完成,子进程还没有执行完,进程组变成孤儿进程组(此时时孤儿进程组中有停止的进程),父进程会向所有子进程发送SIGHUP信号然后发送SIGCONT信号,如果子进程没有处理SIGHUP就会退出,你的start_player_process没有等待子进程的地方(没调用wait或waitpid),所以当父进程先结束,子进程的进程组一旦变成孤儿进程组且有停止的子进程那么子进程直接退出。
五、指针
*p++:地址加1,值不变
++(*p):地址不变,值加1
六、全局变量和局部变量的覆盖问题(new_fd和arg出现的问题)
1、创建线程:ret = pthread_create((pthread_t *)&pthread_id,NULL,(void *)read_client,(void *)&new_fd);
//new_fd作为read_client的参数
2、int new_fd;char *arg;//全局变量
void * read_client(void *arg)
{
char command;
char input[128];
int nbyte,flags;
while((nbyte = read(new_fd,input,sizeof(input))) > 0)
{
command = getCommand(input,arg);
Do_Command(command,arg,mList,shm_addr,pos);
memset(input,0,128);
}
}
一开始我定义了全局变量new_fd,void *arg(全局arg被屏蔽掉了,无关,不用考虑)指向new_fd,当第一次read,getcommand之后,void *arg保存的是歌名,从而new_fd保存的内容也就不再是当前线程的fd,而是歌名,因此再次read时将会出错,直接退出,不再阻塞。
另外如果开了多个线程,就会因为全局变量fd而导致所有的操作只在一个线程内;
**********因此在线程中最好将fd定义为局部变量,来保存当前线程的进程号***********************
**********注意编程规范:不要出现变量重名,防止局部变量隐藏全局变量,或者用::arg来指定使用全局变量*******
修改后的代码 :去掉全局变量,修改歌名变量的命名
//读客户端
void * read_client(void *arg)
{
char command;
char input[128];
int nbyte,flags;
int new_fd = *(int *)arg; //线程中最好用局部变量保存传过来的new_fd
while((nbyte = read(new_fd,input,sizeof(input))) > 0)
{
command = getCommand(input,m_name);
Do_Command(command,m_name,mList,shm_addr,pos);
memset(input,0,128);
}
}
3、段错误
全局变量定义:....int sockfd,pthread_id,new_fd;//new_fd错误地保存了歌名后,不一定会报段错误(段错误一般出现在使用了未定义的地址空间)因为fd定义在最后,可能全局变量区还有空间,虽然超过了四个字节,但编译器不一定会报错